Reputation: 705
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?
Upvotes: 7
Views: 3780
Reputation: 58795
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:
if let
, as above
match
expressions:
match opt {
Some(1) => { ... },
Some(p) => { ... },
None => { ... },
}
match reference {
&1 => { ... },
&p => { ... },
}
In function arguments:
fn foo(&p: &i32) { ... }
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).
Upvotes: 9
Reputation: 23359
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.
Upvotes: 3
Reputation: 13450
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.
Upvotes: 5