rust 在迭代器上调用map时如何消除部分移动

klr1opcd  于 2022-11-12  发布在  其他
关注(0)|答案(3)|浏览(164)

我有一个简单的(我认为应该是)任务将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>>>()
}

Playground permalink
这里有一个部分移动错误,看起来像

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,因为这就是任务的内容。有解决这个错误的方法吗?

lb3vh1jj

lb3vh1jj1#

在2018版的Rust中,闭包总是通过变量名来捕获整个变量,所以传递给内部map的闭包将引用values_info,这是无效的,因为values_info已经被部分移动了(即使闭包不需要访问被移动的部分)。
rust 色2021+
自Rust 2021以来,只捕获闭包主体中所需的最小字段集。这使得原始代码能够按预期工作¹,因此您可以通过简单的changing the playground edition to 2021来消除错误。更多信息请参见edition guide
2015年和2018年生 rust
在以前的版本中,您可以手动执行此操作:首先反构ValuesInfo *,只捕获闭包内部的nameid。一旦得到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()
}

另请参阅

¹除非ValuesInfo实现了Drop,这使得任何解构或部分移动都不健全。

tpgth1q7

tpgth1q72#

在闭包中命名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(),
            }
xuo3flqw

xuo3flqw3#


# [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>>>()
}

也就是说,如果你真的不想复制/克隆Value,你需要事先输入clone name和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>>>()
}

相关问题