Skip to content

Instantly share code, notes, and snippets.

@gregberns
Created December 29, 2018 18:24
Show Gist options
  • Save gregberns/8264e8e7b11e9d881481a7e8a2d5372e to your computer and use it in GitHub Desktop.
Save gregberns/8264e8e7b11e9d881481a7e8a2d5372e to your computer and use it in GitHub Desktop.
Rust - ref/derefs explanation

Thanks to @user#0540 and @Globi#0117 on the #beginners discord

Question: Coming from JS/C#/Haskell and trying to understand the & and * a bit better. In this code snippet, the n in the lambda apparently can either be |&n| n%2==0 or |n| *n%2==0. Could some one explain this a bit better? I think its something to do with reference/defreferece, but still not comfortable with those ideas.

let arr: [u16; 5] = [1, 2, 3, 4, 5];
let mut iterator = arr.iter().filter(|&n| n%2==0);

&n "pattern matches on a reference". Its almost as if:

newtype &a = &{ * :: a } 

Thats not valid haskell but its roughly how ref/derefs behave here

@gb: hmmm, so the ref is kind of like a wrapper/alias that has no runtime impact? not sure I'm following the &{ * :: a }.

@user Imagine you had

newtype Box a = Box { unBox :: a }
-- and
\(Box n) -> n `mod` 2 == 0
-- and 
\box -> unBox box `mod` 2 == 0
-- are the same

@globi:

|n: &u16| ...
|&n: u16| ...

@gregberns: Can you explain a bit about the purpose of the ref stuff? Is that so values dont have to be passed?

@user: Yeah, e.g. if you had HugeDataStructure that contained 50 integers or something. Passing &HugeDataStructure just passes on the address of a HugeDataStructure. Which can be a lot more efficient.

@globi: Also your simple example might be a tiny bit more complicated than it seems: arr.iter() implements Iterator<Item = &u16>, that's already an iterator over a reference but filter then takes a closure that takes the iterator's item by reference again so if you try to annotate n in your closure, you'll see it's actually a &&u16 you only need to dereference it once (to get an &u16) because there is an implementation of % for &u16 and u16 to illustrate, here are 5 equivalents way of writing your filter: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=28031705a4539b0570a5f81c7d233dd1

fn main() {
    let arr: [u16; 5] = [1, 2, 3, 4, 5];
    
    let evens = arr.iter().filter(|n| **n%2==0);
    let evens = arr.iter().filter(|n| *n%2==0); // works with &u16 % u16
    
    let evens = arr.iter().filter(|&n| *n%2==0);
    let evens = arr.iter().filter(|&n| n%2==0); // works with &u16 % u16
    
    let evens = arr.iter().filter(|&&n| n%2==0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment