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: