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`
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.
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:
&
needed to destructure a list of tuples during iteration?If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With