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.
The loop that eats the Vec
Section titled “The loop that eats the Vec”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:
fn main() { let names = vec![ "Ada".to_string(), "Grace".to_string(), "Linus".to_string(), ];
for name in names { println!("hello, {}", name); }
println!("the list is still here: {:?}", names);}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:17help: 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.
Borrow each element with &
Section titled “Borrow each element with &”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):
fn main() { let names = vec![ "Ada".to_string(), "Grace".to_string(), "Linus".to_string(), ];
for name in &names { println!("hello, {}", name); }
println!("the list is still here: {:?}", names);}hello, Adahello, Gracehello, Linusthe 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.
When you need to change the elements
Section titled “When you need to change the elements”&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:
fn main() { let mut names = vec!["Ada".to_string(), "Grace".to_string()];
for name in &mut names { name.push('!'); }
println!("{:?}", names);}["Ada!", "Grace!"]Note the let mut names: writing through a borrow requires the binding itself to be mut.
When consuming is what you want
Section titled “When consuming is what you want”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.
Related
Section titled “Related”- How to pass a Vec to a function without cloning it —
the same
&trick, one call out - How to fix error E0382: borrow of moved value — the error above, in its own page
- Moves and ownership — the rest of this chapter
- Next in this chapter: what is a partial move?