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>>>()
}
Playground permalink
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?
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.
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(),
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With