Iterators, Tuples, and References


#1

This is really just a request for an explanation. With the following code:

fn tuples() {
    let mut vec = Vec::new();
    vec.push((1, 2));
    vec.push((2, 3));
    
    let count = vec.iter().filter( | &&(_,s) | s > 2 ).count();
    
    println!("tuples: {}", count);
}

fn simple() {
    let mut vec = Vec::new();
    vec.push(2);
    vec.push(3);
    
    let count = vec.iter().filter( | &s | *s > 2 ).count();
    
    println!("simple: {}", count);
}

fn double() {
    let mut vec = Vec::new();
    vec.push(2);
    vec.push(3);
    
    let count = vec.iter().filter( | &&s | s > 2 ).count();
    
    println!("double: {}", count);
}

fn main() {
    tuples();
    simple();
    double();
}

The tuples() function makes me think that references to the tuples where actually added to the vector since the filter() method in theory takes a reference to the object for the filtering, and I’m assuming && means a reference to a reference.

The simple() method demonstrates the “normal” filter function using the reference…but then double() is declaring “s” as a reference to a reference (I think)…but then I have no clue why s > 2 works there, I’d assume I would have to do **s > 2 instead…so maybe double ampersand is not a reference to a reference?

Anyone have the skinny on this?


#2

vec.iter() returns an iterator over references to the things in vec. vec has type Vec<(i32, i32)>, so vec.iter() iterates over &(i32, i32). The closure passed to filter takes, as you’ve noted, a reference to the type being iterated over, aka &&(i32, i32) here.

The &s and &&s in the closure arguments aren’t declaring s to be any specific type (its type is fixed). They are rather destructuring the argument. | &s| *s > 2 is equivalent to |s| { let &s = s; *s > 2 } - it’s binding s to the contents of the reference that is being passed as the argument to the closure. Likewise, &&s is binding s to the i32 that’s being referenced by the reference that’s passed as the argument to the closure.


#3

Okay, thank you, that explains some stuff. like when I end up with &&(, ref s) to get a reference to the second element when my initial impulse would be to do &&(, &s), which doesn’t work.

So i’m assuming these two statements are equivalent, they are just changing which side of the assignment operation is doing the destructuring/dereferencing?

|s| { let &s = s; *s > 2 }
|s| { let s = *s; *s > 2 }

#4

Yep!


#5

Just to make more clear: the first variant (let &s = s;) is an assignment with destructuring (that is, "look inside a reference from s and bind it’s content into new s), while the second variant (let s = *s;) is an assignment with dereferencing ("dereference value of s and assign the value it was pointing to to the new s"). The effect is the same, as was explained, just the terminology is a little different.