在迭代器上调用 map 时如何消除部分移动
How to eliminate partial move when calling map on an iterator
我有一个简单的(我认为应该是)任务 map
包含在 Vec
中的值并生成另一个 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>>>()
}
这里我有一个部分移动错误看起来像
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
我需要这个部分 move
因为这就是任务的内容。是否有任何解决方法来解决该错误?
发生这种情况是因为闭包总是按名称捕获整个变量。因此传递给内部 map
的闭包将引用 values_info
,这是无效的,因为 values_info
已经被部分移动(即使闭包不需要访问该部分已移动)。
RFC #2229 更改捕获以借用(或移动)闭包主体中所需的最小字段集。这将使原始代码按预期工作¹。但是,RFC 尚未实施。
相反,您可以手动执行此操作:先解构 ValuesInfo
,然后仅捕获闭包内的 name
和 id
。你可以一拿到就解构ValuesInfo
,在外层闭包的参数列表中:
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()
}
另见
- HashMap borrow issue when trying to implement find or insert 使用类似的技巧解决。
¹ 除非 ValuesInfo
实现 Drop
,这使得任何解构或部分移动都不合理。
在闭包中命名values_info
会借用它
作为一个整体,虽然它已经部分移动
(如错误消息所述)。
您应该事先借用您需要的成员。
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(),
}
#[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>>>()
}
就是说,如果您真的不想 copy/clone Value
,您需要先 clone
名称和 id
。编译器阻止您使用 values_info.name.clone()
,因为函数 into_iter
已经消耗 values_info
。
如果你真的不想复制 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>>>()
}
我有一个简单的(我认为应该是)任务 map
包含在 Vec
中的值并生成另一个 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>>>()
}
这里我有一个部分移动错误看起来像
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
我需要这个部分 move
因为这就是任务的内容。是否有任何解决方法来解决该错误?
发生这种情况是因为闭包总是按名称捕获整个变量。因此传递给内部 map
的闭包将引用 values_info
,这是无效的,因为 values_info
已经被部分移动(即使闭包不需要访问该部分已移动)。
RFC #2229 更改捕获以借用(或移动)闭包主体中所需的最小字段集。这将使原始代码按预期工作¹。但是,RFC 尚未实施。
相反,您可以手动执行此操作:先解构 ValuesInfo
,然后仅捕获闭包内的 name
和 id
。你可以一拿到就解构ValuesInfo
,在外层闭包的参数列表中:
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()
}
另见
- HashMap borrow issue when trying to implement find or insert 使用类似的技巧解决。
¹ 除非 ValuesInfo
实现 Drop
,这使得任何解构或部分移动都不合理。
在闭包中命名values_info
会借用它
作为一个整体,虽然它已经部分移动
(如错误消息所述)。
您应该事先借用您需要的成员。
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(),
}
#[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>>>()
}
就是说,如果您真的不想 copy/clone Value
,您需要先 clone
名称和 id
。编译器阻止您使用 values_info.name.clone()
,因为函数 into_iter
已经消耗 values_info
。
如果你真的不想复制 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>>>()
}