Reputation: 20144
I'm trying to figure out why I'm receiving an error about a moved value for this code segment:
fn get_names(v: Vec<(String, usize)>) -> Vec<String> {
v.iter().cloned()
.map(|(name, _value)| name)
.collect()
}
fn main() {
let v = vec!(("Herman".to_string(), 5));
println!("running");
let names = get_names(v);
assert_eq!(names, ["Herman"]);
println!("{:?}", v);
}
I was reading this weekly rust article and noticed this code block
fn get_names(v: Vec<(String, usize)>) -> Vec<String> {
v.into_iter()
.map(|(name, _score)| name)
.collect()
}
fn main() {
let v = vec!( ("Herman".to_string(), 5));
let names = get_names(v);
assert_eq!(names, ["Herman"]);
}
The syntax makes sense to me, but why would someone want to move out a vector and invalidate v
? Why would you not just create a vector names
and not steal from v
? I attempted modifying the program to only copy the names but i'm getting the error:
src/main.rs:13:22: 13:23 error: use of moved value: `v`
src/main.rs:13 println!("{:?}", v);
^
note: in expansion of format_args!
<std macros>:2:25: 2:56 note: expansion site
<std macros>:1:1: 2:62 note: in expansion of print!
<std macros>:3:1: 3:54 note: expansion site
<std macros>:1:1: 3:58 note: in expansion of println!
src/main.rs:13:5: 13:25 note: expansion site
src/main.rs:10:27: 10:28 note: `v` moved here because it has type `collections::vec::Vec<(collections::string::String, usize)>`, which is non-copyable
src/main.rs:10 let names = get_names(v);
^
error: aborting due to previous error
Could not compile `tutorial`.
Upvotes: 3
Views: 624
Reputation: 98526
The main problem is that you did not change the argument of the function to receive a pointer to the original vector, so it still reveives it as a moved value. So change this line:
fn get_names(v: Vec<(String, usize)>) -> Vec<String>
into this one:
fn get_names(v: &Vec<(String, usize)>) -> Vec<String>
You'll have to change the call accordingly: get_names(&v);
.
Additionally, instead of using cloned()
in the iterator that clones the whole tuples, you might iterate through the pointers and clone just the strings. In this case will not make much difference, but if the object in the vector were more complex it might matter. Something like this:
fn get_names(v: &Vec<(String, usize)>) -> Vec<String> {
v.iter()
.map(|&(ref name, _value)| name.clone())
.collect()
}
Don't forget the ref
into the lambda match expression, because you cannot take ownership of the borrowed members of v
. And don't forget the &
because you are iterating a pointer, so you get pointers to the tuples.
Also, note that you can use the original get_names()
function without invalidating the vector. Just write get_names(v.clone())
!
Upvotes: 4