0

I came across below example in Rust book.

for &item in list.iter() {
    if item > largest {
        largest = item;
    }
}

I suppose it means list.iter() returns the reference to the elements in the list hence &item but while comparing it with largest number why are we not using *item? Also, when I change the &item to item in the first line I am forced to use *item in 2nd and 3rd line by the compiler.

I have seen another example online.

(0..).map(|x| x * x)
    .take_while(|&x| x <= limit)
    .filter(|x| is_even(*x))

Here the closure in take_while accepts &x but uses x directly but the closure in filter takes x without reference but passes *x to is_even.

So how does this work in Rust?

Lukas Kalbertodt
  • 79,749
  • 26
  • 255
  • 305
Akshay Naik
  • 669
  • 1
  • 6
  • 22
  • What you ask is unclear, Could you please provide [MCVE](https://stackoverflow.com/help/mcve) and specify 'x' variable. 'is_even' function signature and the type of 'x' is unknown in the question. – Akiner Alkan Mar 24 '19 at 10:00

1 Answers1

5

What you are seeing here is called destructuring. This is a feature where you can take apart a structure with a pattern.

You probably already saw something like let (a, b) = returns_a_tuple();. Here, a tuple is destructured. You can also destructure references:

// The comments at the end of the line tell you the type of the variable
let i = 3;                // : i32
let ref_i = &i;           // : &i32
let ref_ref_i = &ref_i;   // : &&i32

let &x = ref_i;       // : i32
let &y = ref_ref_i;   // : &i32
let &&z = ref_ref_i;  // : i32

// All of these error because we try to destructure more layers of references
// than there are.
let &a = i;
let &&b = ref_i;
let &&&c = ref_ref_i;

This has the counter-intuitive effect that the more & you add in the pattern, the fewer & will the type of the variable have. But it does make sense in the context of destructuring: when you already mention the structure in the pattern, the structure won't be in the bound variables anymore.

(It is worth noting that this "destructuring references away" only works with referee types that are Copy. Otherwise you will get a "cannot move out of borrowed content" error.)


Now what does that have to do with your for loop and the closures? Turns out: patterns are everywhere. The slot between for and in in the for loop is a pattern, and arguments of functions and closures are pattern as well! This works:

// Destructuring a tuple in the for loop pattern
let v = vec![3];
for (i, elem) in v.iter().enumerate() {}

// Destructuring an array in the function argument (works the same for closures)
fn foo([x, y, z]: [f32; 3]) {}

I suppose it means list.iter() returns the reference to the elements in the list

Exactly.

... hence &item

"hence" is not correct here. The author of this code didn't want to work with the reference to the item, but instead work with the real value. So they added the & in the pattern to destructure the reference away.

but while comparing it with largest number why are we not using *item?

Yes, because the reference was already removed by the destructuring pattern.

Also, when I change the &item to item in the first line I am forced to use *item in 2nd and 3rd line by the compiler.

Yes, because now the pattern doesn't destructure the reference anymore, so item is a reference again. This is the basic gist with all of this: most of the time you can either remove the reference in the pattern by adding a & or you can remove the reference when using the variable by adding a *.

Here the closure in take_while accepts &x but uses x directly but the closure in filter takes x without reference but passes *x to is_even.

It should be clear by now why that is, right? The take_while closure removes the reference via destructuring in the pattern while the filter closure does it via standard dereferencing.


You can read more about all of this in this chapter of the book.

Lukas Kalbertodt
  • 79,749
  • 26
  • 255
  • 305