Reputation: 27425
I have a simple (I thought it should be) task to map
values contained in a Vec
and produce another Vec
:
#[derive(Clone)]
struct Value(u32);
#[derive(Clone)]
struct Id(u32);
struct ValuesInfo {
values: Vec<Value>,
name: String,
id: Id
}
struct ValueInfo{
value: Value,
name: String,
id: Id
}
fn extend_values(v: Vec<ValuesInfo>) -> Vec<Vec<ValueInfo>> {
v.into_iter().map(|values_info|{
values_info.values.into_iter().map(|value|{
ValueInfo{
value,
name: values_info.name.clone(),
id: values_info.id.clone()
}
}).collect::<Vec<ValueInfo>>()
}).collect::<Vec<Vec<ValueInfo>>>()
}
Here I have a partial move error looking as
Compiling playground v0.0.1 (/playground)
error[E0382]: borrow of moved value: `values_info`
--> src/lib.rs:20:44
|
20 | values_info.values.into_iter().map(|value|{
| ----------- ^^^^^^^ value borrowed here after partial move
| |
| `values_info.values` moved due to this method call
...
23 | name: values_info.name.clone(),
| ----------- borrow occurs due to use in closure
|
note: this function consumes the receiver `self` by taking ownership of it, which moves `values_info.values`
= note: move occurs because `values_info.values` has type `std::vec::Vec<Value>`, which does not implement the `Copy` trait
error: aborting due to previous error
I need this partial move
because this is what the task is about. Is there any workaround to solve the error?
Upvotes: 5
Views: 3150
Reputation: 28005
In the 2018 edition of Rust, closures always capture entire variables by name. So the closure passed to the inner map
would take a reference to values_info
, which is not valid because values_info
has already been partially moved (even though the closure does not need access to the part that was moved).
Since Rust 2021, captures borrow (or move) only the minimal set of fields required in the body of the closure. This makes the original code work as you expected¹, so you can make the error go away by simply changing the playground edition to 2021. See the edition guide for more.
In previous editions, you can do it manually: destructure the ValuesInfo
first, and only capture name
and id
inside the closure. You can destructure the ValuesInfo
as soon as you get it, in the argument list of the outer closure:
fn extend_values(v: Vec<ValuesInfo>) -> Vec<Vec<ValueInfo>> {
v.into_iter()
.map(|ValuesInfo { values, name, id }| {
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ like `let ValuesInfo { values, name, id } = values_info;`
values
.into_iter()
.map(|value| ValueInfo {
value,
name: name.clone(),
id: id.clone(),
})
.collect()
})
.collect()
}
¹ Unless ValuesInfo
implements Drop
, which makes any destructuring or partial move unsound.
Upvotes: 8
Reputation: 16925
Naming values_info
in the closure will borrow it
as a whole although it is already partially moved
(as told by the error message).
You should borrow the members you need beforehand.
let name=&values_info.name;
let id=&values_info.id;
values_info.values.into_iter().map(|value|{
ValueInfo{
value,
name: name.clone(),
id: id.clone(),
}
Upvotes: 4
Reputation: 423
#[derive(Clone)] //you could derive Copy. u32 is generally cheap to copy
struct Value(u32);
#[derive(Clone)] //you could derive Copy
struct Id(u32);
struct ValuesInfo {
values: Vec<Value>,
name: String,
id: Id
}
struct ValueInfo{
value: Value,
name: String,
id: Id
}
//No need to consume v. Not sure if it will come in handy
fn extend_values(v: &Vec<ValuesInfo>) -> Vec<Vec<ValueInfo>> {
//Use into_iter only if you need the ownership of the content of the array
// which I don't think you need because you are cloning every value of ValueInfo
//except for value which is already cheep to copy/clone
v.iter().map(|values_info|{
values_info.values.iter().map(|value|{
ValueInfo{
value: value.clone(),
name: values_info.name.clone(),
id: values_info.id.clone()
}
}).collect::<Vec<ValueInfo>>()
}).collect::<Vec<Vec<ValueInfo>>>()
}
That said, if you really don't want to copy/clone Value
you need to clone
name and id
before hand. The compiler stops you from using values_info.name.clone()
because the function into_iter
already consumed values_info
.
The code would look something like this if you really don't want to copy Value
#[derive(Clone)]
struct Value(u32);
#[derive(Clone)]
struct Id(u32);
struct ValuesInfo {
values: Vec<Value>,
name: String,
id: Id
}
struct ValueInfo{
value: Value,
name: String,
id: Id
}
fn extend_values(v: Vec<ValuesInfo>) -> Vec<Vec<ValueInfo>> {
v.into_iter().map(|values_info|{
let name = values_info.name.clone();
let id = values_info.id.clone();
values_info.values.into_iter().map(
|value| {
ValueInfo{
value,
name: name.clone(),
id: id.clone(),
}
}).collect::<Vec<ValueInfo>>()
}).collect::<Vec<Vec<ValueInfo>>>()
}
Upvotes: 2