How to fix ownership errors with a Vec
Short answer: Indexing never hands you the element, only a place to look at it, so
let name = names[0]; will not compile when the element type is not Copy. Borrow it —
&names[0] — or take it out with a method that owns it: remove, pop, swap_remove, or
std::mem::take.
The error: an element will not move out of a Vec
Section titled “The error: an element will not move out of a Vec”Every block on this page is a complete src/main.rs. Start from any Cargo project — cargo new vec-own will do, nothing in the output depends on the name — and replace the file one block at a
time. This first one fails:
fn main() { let names = vec![String::from("Ada"), String::from("Grace")]; let name = names[0]; println!("hello, {name}");}error[E0507]: cannot move out of index of `Vec<String>` --> src/main.rs:3:16 |3 | let name = names[0]; | ^^^^^^^^ move occurs because value has type `String`, which does not implement the `Copy` trait |help: consider borrowing here |3 | let name = &names[0]; | +help: consider cloning the value if the performance cost is acceptable |3 | let name = names[0].clone(); | ++++++++
For more information about this error, try `rustc --explain E0507`.Indexing a Vec is sugar for a call to Index::index, which takes &self and returns &T. The
expression names[0] is a place behind a shared reference, and moving a String out of a place
you do not own is what the borrow checker is for: the Vec owns the buffer, and a String lifted
out of the middle of it would leave a hole. The two help: lines are the two cheap ways out —
borrow the element, or clone it (when should you clone?)
— and borrowing is the first half of this page.
Borrow the element
Section titled “Borrow the element”fn main() { let mut names = vec![String::from("Ada"), String::from("Grace")];
let name = &names[0]; println!("hello, {name}");
names[1].push_str(" Hopper"); println!("{names:?}");}hello, Ada["Ada", "Grace Hopper"]&names[0] borrows the element instead of moving it: name is a &String, and the Vec keeps
every byte it owns. names[1].push_str(...) writes through the same mechanism, and the let mut names is what makes that legal — a mutable borrow needs a mutable binding. When you need every
element rather than one, the same fix at loop scale is for name in &names
(how to loop over a Vec without moving it).
Take the element out
Section titled “Take the element out”Borrowing leaves the element where it is. When the element needs to leave the Vec for good, use
a method that takes the Vec by &mut self and gives you the value:
fn main() { let mut names = vec![ String::from("Ada"), String::from("Grace"), String::from("Linus"), String::from("Barbara"), ];
let first = names.remove(0); println!("remove(0) took {first}, left {names:?}");
let first = names.swap_remove(0); println!("swap_remove(0) took {first}, left {names:?}");}remove(0) took Ada, left ["Grace", "Linus", "Barbara"]swap_remove(0) took Grace, left ["Barbara", "Linus"]remove(i) shifts every later element down a slot, so the order survives and the cost grows with
the length. swap_remove(i) fills the slot with the last element and pops it off — constant time,
but the order does not survive, which is visible above: Barbara is first now. pop() is the same
idea from the end, and it returns an Option<T>.
Sometimes the slot has to stay. std::mem::take replaces the element with its type’s default and
hands you the old value, so the Vec keeps its length:
use std::mem;
fn main() { let mut names = vec![String::from("Ada"), String::from("Grace")]; let name = mem::take(&mut names[0]); println!("took {name:?}, left {names:?}");}took "Ada", left ["", "Grace"]take needs T: Default — String’s default is the empty string — and a &mut to the element,
which is why the Vec binding needs mut as well.
When there is no error at all
Section titled “When there is no error at all”If the element type is Copy — i32, bool, f64 — names[0] copies the element out and the
original line compiles, because a copy is not a move
(how to tell whether a type will move or copy).
The error above is a String problem, not a Vec problem.
One more distinction: indexing an index that is not there panics at runtime. That is a different
failure with a different fix — get returns an Option — and it is not an ownership error.
Related
Section titled “Related”- How to loop over a Vec without moving it — the same fix, for every element at once
- How to pass a Vec to a function without cloning it —
the same
&, one call out - When should you clone? — when you need an owned copy of the element
- Moves and ownership — the rest of this chapter
- Next in this chapter: how does Rust manage memory without a garbage collector?