Skip to content
Select theme

How to loop over a Vec without moving it

Short answer: Write for name in &names instead of for name in names. The & makes the loop borrow each element instead of consuming the whole Vec, so the Vec is still usable afterwards.

A for loop over a Vec by value takes ownership of it. for name in names is sugar for names.into_iter(), and into_iter consumes its receiver: each String is moved out of the buffer into name, and when name is dropped at the end of the iteration that element is gone. Use the Vec after the loop and the borrow checker stops you:

error[E0382]: borrow of moved value: `names`
--> src/main.rs:12:46
|
2 | let names = vec![
| ----- move occurs because `names` has type `Vec<String>`, which does not implement the `Copy` trait
...
8 | for name in names {
| ----- `names` moved due to this implicit call to `.into_iter()`
...
12 | println!("the list is still here: {:?}", names);
| ^^^^^ value borrowed here after move
|
note: the `for` loop is desugared into a call to `std::iter::IntoIterator::into_iter`, which takes ownership of the receiver `self`, which moves `names`
--> /rustc/b940084d7eb6a299eb4bfeb8e34901bc051e7ac4/library/core/src/iter/traits/collect.rs:312:17
help: consider iterating over a slice of the `Vec<String>`'s content to avoid moving into the `for` loop
|
8 | for name in &names {
| +
For more information about this error, try `rustc --explain E0382`.

The note: line is the whole mechanism: a for loop is a call to IntoIterator::into_iter, which takes self by value. The help: line is the fix, and it is one character.

Add the & and the same program compiles and runs. Put it in src/main.rs of any Cargo project (cargo new loop-demo will do — the name does not matter because nothing in the output depends on it):

hello, Ada
hello, Grace
hello, Linus
the list is still here: ["Ada", "Grace", "Linus"]

&names is a &Vec<String>, and &Vec<T> implements IntoIterator by yielding &T, so here name is a &String, not a String. Nothing moves and nothing is dropped; the loop reads through references and hands the Vec back untouched. for name in &names and for name in names.iter() are the same loop spelled two ways.

It is the same & as passing a Vec to a function: the value stays where it is, and the reader gets a pointer to it.

&mut names — or names.iter_mut(), the same loop — borrows mutably instead. The loop body can then write through the reference, and the Vec survives the loop just the same:

["Ada!", "Grace!"]

Note the let mut names: writing through a borrow requires the binding itself to be mut.

for name in names is the right loop when the Vec is not needed afterwards. Moving the elements out one at a time is usually cheaper than cloning them, and it is the version that leaves nothing behind. Decide with one question: do you need the Vec after the loop? If yes, borrow it. The rules behind & and &mut are in the Borrowing book.