What is the difference between let r and let &r;

What is the difference between these three?

  1. let mut i = 55;
    let r = &i;
    

and

  1. let mut i = 55;
    let &r = &I;
    

and

  1. let mut i = 55;
    let &(mut r) = &i;
    

if you go into this chapter: Generic Types, Traits, and Lifetimes - The Rust Programming Language

first its given

let mut largest = &number_list[0];
for number in &number_list {

then again this is made into function as:

let mut largest = &list[0];
for item in list {

what is the difference between in &number_list and in list?

on the left side of let statements, i.e.

let …this place… = …;
    ^^^^^^^^^^^^

you’re using a so-called pattern. Pattern matching in Rust is most prominent in match syntax, or if let …, but normal let supports certain patterns, too. Mutability markers (i.e. the mut of let mut x = …) is also part of pattern syntax btw.

Anyway, there’s the &… pattern which matches against a reference, dereferences it, and matches the result against the pattern ….

Hence

let &r = &i;

has the effect of taking the reference &i (of type &i32), dereferencing it, and then assigning the resulting i32 to a new variable r.

With

let &(mut r) = &i;

instead, that new variable is also mutable.

You don’t really need to use this syntax though, it’s perhaps easier to just dereference using the * operator on the right side of the let statement.

// instead of
let &r = &i;

// you can do
let r = *&i;

// or in this case
let r = i;
// because the `*` and `&` steps effectively “cancel out”
// instead of
let &(mut r) = &i;

// you can do
let mut r = *&i;

// or in this case
let mut r = i;
4 Likes

It’s essentially the same thing in both cases, the context is important.

In the first example, number_list is a Vec<i32>. In the second example, list is a function argument of type &[i32]. If you ignore the Vec<i32> vs. [i32] distinction, notably, in the second case list already is a reference.


It doesn’t have to always be a reference. Iteration with for in Rust (which works with any type supporting the IntoIterator trait) typically supports 3 different modes: by value, consuming the collection and yielding owned items, by-reference, taking the collection by reference, and yielding &T items, and by mutable reference (same idea as before, but add the word “mutable” everywhere).

These examples each used a reference (&Vec<i32> or &[i32]) to a collection, so the type of number/item will be &i32, too.

2 Likes

The simple answer is that it doesn't matter because you should never use let &r = (nor let ref r =).

Those are there for certain situations in nested patterns, but at the top level it's considered good practice to not use them.

(Once you hit let Some(&r) = or let Some(ref r) = they're easier to explain because they're not useless there.)

4 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.