When should add & in for loop before the variable?

Hello,
In the book of The Rust Programming Language, there is an example in which there is an & before the virable in the for loop. Why should add & here?

fn first_word(s:&String)->usize{
       let bytes=s.as_bytes();
       for (i, &item) in bytes.iter().enumerate(){
           if item==b' '{
                    return i;
              }
 }
 s.len();
}

Why should add & here and what the difference between having it and not having it?

Another example made

fn main(){
    let a=[1,2,3];
    for each in a {
        println!("{each}");
    }
}

Why can't add the & before the each?

TLDR:

these are all semantically the same:

for (i, &item) in bytes.iter().enumerate() {
  //...
}

for (i, item) in bytes.iter().enumerate() {
  let &item = item;
  //...
}

for (i, item) in bytes.iter().enumerate() {
  let item = *item;
  //...
}

long answer:

although a variable declaration seems innocently simple, let-bindings in rust actually use "patterns" syntax, it's just the most common and simplest pattern is IdentifierPattern. for loops are another sugarred syntax that also introduce variable bindings.

in its simplest case, & in front of a variable name can be translated to the dereference operator (i.e. unary *) on the right hand side. consider this example (with explicit type):

fn foo(x: &i32) {
    let &y: &i32 = x;
}

the left side of the = sign is the pattern (you can read it as: &x has type &i32, which indicates x should have type i32), and the right side of the = sign is an expression (which the pattern is matched against). it's equivalent to:

fn foo(x: &i32) {
    let x: i32 = *x;
}
4 Likes

Although it doesn't cover binding modes or ref patterns, this is my favorite introduction to patterns:

https://h2co3.github.io/pattern/

And an example:

use std::any::type_name_of_val;

fn example(reference: &i32, option: Option<i32>) {
    match option {
        Some(i) => println!("i is an {}", type_name_of_val(&i)), // i32
        None => {},
    }
    match reference {
        &i => println!("i is an {}", type_name_of_val(&i)), // i32
    }

    let Some(i) = option else { unreachable!() };
    println!("i is an {}", type_name_of_val(&i)); // i32

    let &i = reference;
    println!("i is an {}", type_name_of_val(&i)); // i32
}

fn main() {
    example(&0, Some(0));
}

You can "destructure" a reference with a &name pattern just like you can destructure an Option with a Some(name) pattern.

2 Likes

So, why does this code cause error:

fn main(){
    let a=[1,2,3];
    for &each in a {
        println!("{each}");
    }
}

for is based on the IntoIterator trait, and the implementation for [i32; 3] arrays returns i32s, not &i32s. You can't destructure a non-reference with a &each pattern. Just like you can't do this either:

fn main(){
    let a=[1,2,3];
    for Some(each) in a {
        println!("{each}");
    }
}

But why this works

for (i, &item) in bytes.iter().enumerate(){
           if item==b' '{
                    return i;
              }

Because bytes.iter().enumerate() returns (usize, &u8).

1 Like

How did you know this?

Experience, but let's pretend I didn't have that. How could I find it out?

Here's the easy way: make an assignment or similar that you know won't work, and the compiler will generally tell you what the type is.

fn first_word(s: &String) -> usize {
    let bytes = s.as_bytes();
    for something in bytes.iter().enumerate() {
        let () = something; // <-- assignment you know will fail
        let (i, &item) = something;
        if item == b' ' {
            return i;
        }
    }
    s.len();
}
4 |         let () = something;
  |             ^^   --------- this expression has type `(usize, &u8)`

Let me lay some breadcrumbs so you can build your own experience more methodically, though. First of all, from this thread itself: if (i, &item) works, the iteration type must be a 2-typle with the last element being some sort of reference, so that's a pretty good hint on its own.

Another thing you will learn with experience is that when you have an iterator chain like bytes.iter().enumerate(), the end of the chain is probably going to be a method on the Iterator trait. You could look up those methods in the documentation. In this case, that's enumerate. If you hover on the β“˜, it will tell you:

Notable traits for Enumerate<I>

impl<I> Iterator for Enumerate<I>
where
    I: Iterator,
type Item = (usize, <I as Iterator>::Item);

What does this mean? It means the iterator item type is a 2-tuple (like we figured out already), and the last element is the same as the previous iterator in the chain. So now we need to know what bytes.iter()'s iterator item type is.

Over time, you'll get to know many common iterator methods well enough you won't have to look them up.

Next bit of experience: by convention, .iter() will give an iterator over shared references (&_), and .iter_mut() will give an iterator over exclusive references (&mut _). If you knew that convention, you could make a good guess that bytes.iter() has &u8 as the iterator item, and you'd be correct.

If you wanted to look that up in the documentation, you could

You have a &[u8], so the iterator item type is &u8, indeed.

If you didn't know you had a &[u8]...

You have s: &String and bytes = s.as_bytes(). What does that do?

So bytes is a &[u8] since that's what as_bytes returns.

Or instead of chasing documentation, use the assignment trick again.

    let bytes: () = s.as_bytes();
2 |     let bytes: () = s.as_bytes();
  |                --   ^^^^^^^^^^^^ expected `()`, found `&[u8]`
5 Likes

Oddly enough that is a question that frequently passes through my mind as I come hit squirrelly issues in my Rust. Or read most answers to questions here.

But even if it is [i32], it can be borrowed by &[i32], so doesn't

fn main(){
    let a=[1,2,3];
    for &each in a {
        println!("{each}");
    }
}

work?

As I said before, the IntoIterator implementation for [i32; 3] has i32 as the Item type. So no, it doesn't work.

There's an IntoIterator implementation for &[i32; 3] too. That one does have &i32 as the Item type. So this works:

    let a = [1,2,3];
    // new       v
    for &each in &a {
        println!("{each}");
    }

Some expressions auto-ref (automatically add a reference), such as method call expressions. But most expressions do not. For those, if you want something borrowed, you have to spell it out by adding the & yourself. Like I did above: for doesn't auto-ref.

1 Like

For convenience, here's a link to the section of The Book that presents the first code example in this discussion: The Slice Type.

Quoting from that section:

The first_word function has a &String as a parameter. We don’t want ownership, so this is fine.

That characterization that it is "fine" seems a little vague to me as a beginner, but the replies here help explain it a little better, thanks.

EDIT:

The link above appears to lead to beta content; this one may be more stable:

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.