Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the purpose of `&` before the loop variable?

What is the purpose of & in the code &i in list? If I remove the &, it produces an error in largest = i, since they have mismatched types (where i is &32 and i is i32). But how does &i convert i into i32?

fn largest(list: &[i32]) -> i32 {
    println!("{:?}", list);
    let mut largest = list[0];
    for &i in list {
        if i > largest {
            largest = i;
        }
    }
    largest
}

fn main() {
    let hey = vec![1, 3, 2, 6, 90, 67, 788, 12, 34, 54, 32];
    println!("The largest number is: {}", largest(&hey));
}

Playground

It seems like it is somehow dereferencing, but then why in the below code, is it not working?

fn main() {
    let mut hey: i32 = 32;
    let x: i32 = 2;
    hey = &&x;
}

It says:

4 |     hey = &&x;
  |           ^^^ expected i32, found &&i32
  |
  = note: expected type `i32`
             found type `&&i32`
like image 424
Muhammad Naufil Avatar asked Aug 03 '19 14:08

Muhammad Naufil


2 Answers

So normally when you use for i in list, the loop variable i would be of type &i32.

But when instead you use for &i in list, you are not dereferencing anything, but instead you are using pattern matching to explicitly destructure the reference and that will make i just be of type i32.

See the Rust docs about the for-loop loop variable being a pattern and the reference pattern that we are using here. See also the Rust By Example chapter on destructuring pointers.

 

An other way to solve this, would be to just keep i as it is and then comparing i to a reference to largest, and then dereferencing i before assigning to largest:

fn largest(list: &[i32]) -> i32 {
    println!("{:?}", list);
    let mut largest = list[0];
    for i in list {
        if i > &largest {
            largest = *i;
        }
    }
    largest
}

 


fn main() {
    let mut hey: i32 = 32;
    let x: i32 = 2;
    hey = &&x;
}

This simply doesn't work, because here you are assigning hey, which is an i32, to a reference to a reference to an i32. This is quite unrelated to the pattern matching and destructuring in the loop variable case.

like image 133
ruohola Avatar answered Oct 01 '22 02:10

ruohola


This is the effect of destructuring. I won't completely describe that feature here, but in short:

In many syntax contexts (let bindings, for loops, function arguments, ...) , Rust expects a "pattern". This pattern can be a simple variable name, but it can also contain some "destructuring elements", like &. Rust will then bind a value to this pattern. A simple example would be something like this:

let (a, b) = ('x', true);

On the right hand side there is a value of type (char, bool) (a tuple). This value is bound to the left hand pattern ((a, b)). As there is already a "structure" defined in the pattern (specifically, the tuple), that structure is removed and a und b bind to the tuple's elements. Thus, the type of a is char and the type of b is bool.

This works with a couple of structures, including arrays:

let [x] = [true];

Again, on the right side we have a value of type [bool; 1] (an array) and on the left side we have a pattern in the form of an array. The single array element is bound to x, meaning that the type of x is bool and not [bool; 1]!

And unsurprisingly, this also works for references!

let foo = 0u32;
let r = &foo;
let &c = &foo;

Here, foo has the type u32 and consequently, the expression &foo has the type &u32. The type of r is also &u32, as it is a simple let binding. The type of c is u32 however! That is because the "reference was destructured/removed" by the pattern.

A common misunderstanding is that syntax in patterns has exactly the opposite effect of what the same syntax would have in expressions! If you have a variable a of type [T; 1], then the expression [a] has the type [[T; 1]; 1] → it adds stuff. However, if you bind a to the pattern [c], then y has the type T → it removes stuff.

let a = [true];    // type of `a`: `[bool; 1]`
let b = [a];       // type of `b`: `[[bool; 1]; 1]`
let [c] = a;       // type of `c`: `bool`

This also explains your question:

It seems like it is somehow dereferencing, but then why in the below code, it is not working?

fn main() {
   let mut hey:i32 = 32;
   let x:i32 = 2;
   hey = &&x;
}

Because you use & on the expression side, where it adds a layer of references.


So finally about your loop: when iterating over a slice (as you do here), the iterator yields reference to the slice's elements. So in the case for i in list {}, i has the type &i32. But the assignment largest = i; requires a i32 on the right hand side. You can achieve this in two ways: either dereference i via the dereference operator * (i.e. largest = *i;) or destructure the reference in the loop pattern (i.e. for &i in list {}).


Related questions:

  • Iterating over a slice's values instead of references in Rust?
  • Why is & needed to destructure a list of tuples during iteration?
like image 25
Lukas Kalbertodt Avatar answered Oct 01 '22 04:10

Lukas Kalbertodt