Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Referencing / dereferencing a vector element in a for loop

In the code below, I want to retain number_list, after iterating over it, since the .into_iter() that for uses by default will consume. Thus, I am assuming that n: &i32 and I can get the value of n by dereferencing.

fn main() {
    let number_list = vec![24, 34, 100, 65];
    let mut largest = number_list[0];

    for n in &number_list {
        if *n > largest {
            largest = *n;
        }
    }

    println!("{}", largest);
}

It was revealed to me that instead of this, we can use &n as a 'pattern':

fn main() {
    let number_list = vec![24, 34, 100, 65];
    let mut largest = number_list[0];

    for &n in &number_list {
        if n > largest {
            largest = n;
        }
    }

    println!("{}", largest);
    number_list;
}

My confusion (and bear in mind I haven't covered patterns) is that I would expect that since n: &i32, then &n: &&i32 rather than it resolving to the value (if a double ref is even possible). Why does this happen, and does the meaning of & differ depending on context?

like image 280
njp Avatar asked Sep 06 '18 11:09

njp


3 Answers

It can help to think of a reference as a kind of container. For comparison, consider Option, where we can "unwrap" the value using pattern-matching, for example in an if let statement:

let n = 100;
let opt = Some(n);

if let Some(p) = opt {
    // do something with p
}

We call Some and None constructors for Option, because they each produce a value of type Option. In the same way, you can think of & as a constructor for a reference. And the syntax is symmetric:

let n = 100;
let reference = &n;

if let &p = reference {
    // do something with p
}

You can use this feature in any place where you are binding a value to a variable, which happens all over the place. For example:

  1. if let, as above

  2. match expressions:

    match opt {
        Some(1) => { ... },
        Some(p) => { ... },
        None    => { ... },
    }
    match reference {
        &1 => { ... },
        &p => { ... },
    }
    
  3. In function arguments:

    fn foo(&p: &i32) { ... }
    
  4. Loops:

    for &p in iter_of_i32_refs {
        ...
    }
    

And probably more.

Note that the last two won't work for Option because they would panic if a None was found instead of a Some, but that can't happen with references because they only have one constructor, &.

does the meaning of & differ depending on context?

Hopefully, if you can interpret & as a constructor instead of an operator, then you'll see that its meaning doesn't change. It's a pretty cool feature of Rust that you can use constructors on the right hand side of an expression for creating values and on the left hand side for taking them apart (destructuring).

like image 195
Peter Hall Avatar answered Nov 11 '22 23:11

Peter Hall


As apart from other languages (C++), &n in this case isn't a reference, but pattern matching, which means that this is expecting a reference.
The opposite of this would be ref n which would give you &&i32 as a type.

This is also the case for closures, e.g.

(0..).filter(|&idx| idx < 10)...

Please note, that this will move the variable, e.g. you cannot do this with types, that don't implement the Copy trait.

like image 4
hellow Avatar answered Nov 11 '22 22:11

hellow


My confusion (and bear in mind I haven't covered patterns) is that I would expect that since n: &i32, then &n: &&i32 rather than it resolving to the value (if a double ref is even possible). Why does this happen, and does the meaning of & differ depending on context?

When you do pattern matching (for example when you write for &n in &number_list), you're not saying that n is an &i32, instead you are saying that &n (the pattern) is an &i32 (the expression) from which the compiler infers that n is an i32.

Similar things happen for all kinds of pattern, for example when pattern-matching in if let Some (x) = Some (42) { /* … */ } we are saying that Some (x) is Some (42), therefore x is 42.

like image 2
Jmb Avatar answered Nov 12 '22 00:11

Jmb