How to pass a Vec to a function without cloning it
Short answer: Make the parameter &[T] and pass &vec. The function reads the buffer the Vec
already owns, nothing is copied, and the Vec is still yours after the call.
Passing by value moves the Vec
Section titled “Passing by value moves the Vec”A parameter typed Vec<i32> takes ownership of the argument, the same way an assignment does —
How to pass ownership of a value to a function
is that rule. The call moves the Vec into total, so the caller’s binding is dead afterwards:
fn total(scores: Vec<i32>) -> i32 { scores.iter().sum()}
fn main() { let scores = vec![10, 20, 30];
println!("total: {}", total(scores)); println!("count: {}", scores.len());}error[E0382]: borrow of moved value: `scores` --> src/main.rs:9:27 |6 | let scores = vec![10, 20, 30]; | ------ move occurs because `scores` has type `Vec<i32>`, which does not implement the `Copy` trait7 |8 | println!("total: {}", total(scores)); | ------ value moved here9 | println!("count: {}", scores.len()); | ^^^^^^ value borrowed here after move |note: consider changing this parameter type in function `total` to borrow instead if owning the value isn't necessary --> src/main.rs:1:18 |1 | fn total(scores: Vec<i32>) -> i32 { | ----- ^^^^^^^^ this parameter takes ownership of the value | | | in this functionhelp: consider cloning the value if the performance cost is acceptable |8 | println!("total: {}", total(scores.clone())); | ++++++++
For more information about this error, try `rustc --explain E0382`.The move happens at the call, on line 8 — the arrow points at the argument, not at anything inside
total. The note: is the useful part: rustc’s first suggestion is to make the parameter a borrow,
which is the rest of this page. Its second suggestion, scores.clone(), also compiles, and for a
Vec<i32> of three elements it would even be fast — but it allocates a second buffer on every call,
and it is the wrong default.
Borrow instead: &[T]
Section titled “Borrow instead: &[T]”Change the parameter to &[i32] and the same program compiles and runs:
fn total(scores: &[i32]) -> i32 { scores.iter().sum()}
fn main() { let scores = vec![10, 20, 30];
println!("total: {}", total(&scores)); println!("count: {}", scores.len());}total: 60count: 3&scores is a &Vec<i32>; where a &[i32] is expected, deref coercion turns it into a slice
reference, and &scores[..] is the explicit spelling. No second buffer exists: the parameter points
at the same heap allocation the Vec already had, and scores.len() works on the next line because
ownership never left main. Nothing is freed when total returns either, because dropping a
reference does nothing.
Inside the function, a &[i32] supports everything a reading function needs: iter(), len(),
indexing and first() all work. Those methods belong to slices, and a Vec exposes them through
Deref, so nothing is lost by taking the slice instead. What a &[i32] cannot do is grow: no push,
no pop. A function that appends needs &mut Vec<T>, which belongs to the Borrowing
book.
Why &[T] and not &Vec<T>
Section titled “Why &[T] and not &Vec<T>”A &Vec<i32> parameter also compiles, but it demands that every caller hand over a Vec. A &[i32]
parameter accepts anything that can be viewed as a slice:
fn first(scores: &[i32]) -> Option<i32> { scores.first().copied()}
fn main() { let scores = vec![10, 20, 30];
println!("from a Vec: {:?}", first(&scores)); println!("from an array: {:?}", first(&[4, 5, 6])); println!("from a slice: {:?}", first(&scores[1..]));}from a Vec: Some(10)from an array: Some(4)from a slice: Some(20)With &Vec<i32> the array line would not compile, because an array is not a Vec; a caller holding a
slice of a larger buffer would have to allocate a Vec just to call you. If the function only reads,
&[T] is the signature that costs callers the least.
Pass by value when the function keeps the value, and clone when the value has to exist in two places at once — When should you clone? sorts those cases out.
Related
Section titled “Related”- How to pass ownership of a value to a function — the move this page avoids
- When should you clone? — when the copy is still the right answer
- How to fix error E0382: borrow of moved value — the error the by-value version produces
- Moves and ownership — the rest of this chapter
- Next in this chapter: how to loop over a Vec without moving it