根据最后一个元素将某物推入向量

8

我想得到一个向量的最后一个元素,并将其用于确定要推入的下一个元素。这里有一个示例,说明了它不起作用的方式,但展示了我正在尝试实现的内容:

let mut vector: Vec<i32> = Vec::new();

if let Some(last_value) = vector.last() {
    vector.push(*last_value + 1);
}

当向量被不可变地借用时,我无法使用 push

error[E0502]: cannot borrow `vector` as mutable because it is also borrowed as immutable
 --> src/main.rs:5:9
  |
4 |     if let Some(last_value) = vector.last() {
  |                               ------ immutable borrow occurs here
5 |         vector.push(*last_value + 1);
  |         ^^^^^^ mutable borrow occurs here
6 |     }
  |     - immutable borrow ends here

什么是一个好的方法来做到这一点?
1个回答

10

非词法生命周期之后

您的原始代码可以在启用非词法生命周期的Rust 2018中直接使用:

fn main() {
    let mut vector: Vec<i32> = Vec::new();

    if let Some(last_value) = vector.last() {
        vector.push(*last_value + 1);
    }
}

借用检查器已经得到改进,能够意识到last_value中的引用与需要对vector进行可变借用以推入新值不重叠。

请参见Returning a reference from a HashMap or Vec causes a borrow to last beyond the scope it's in?,其中涉及一个类似的情况,但是在Rust 1.32中(截至目前)借用检查器尚不能很好地处理它。

Non-Lexical Lifetimes之前

vector.last()的结果为Option<&i32>。该值中的引用会使向量被借用。我们需要在向量上推入元素之前摆脱向量中的所有引用。

如果您的向量包含可Copy的值,请将该值从向量中复制出来以更快地结束borrow。

fn main() {
    let mut vector: Vec<i32> = Vec::new();

    if let Some(&last_value) = vector.last() {
        vector.push(last_value + 1);
    }
}

这里,我使用了模式Some(&last_value)代替Some(last_value)。这个模式对引用进行了解构并强制进行了复制。如果您尝试将此模式用于不可Copy的类型,则会出现编译器错误:

error[E0507]: cannot move out of borrowed content
 --> src/main.rs:4:17
  |
4 |     if let Some(&last_value) = vector.last() {
  |                 ^----------
  |                 ||
  |                 |hint: to prevent move, use `ref last_value` or `ref mut last_value`
  |                 cannot move out of borrowed content

如果你的向量不包含可 Copy 的类型,你可能需要先克隆该值:

fn main() {
    let mut vector: Vec<String> = Vec::new();

    if let Some(last_value) = vector.last().cloned() {
        vector.push(last_value + "abc");
    }
}

或者你可以以另一种方式转换值,使得.map()调用返回一个不从向量借用的值。

fn main() {
    let mut vector: Vec<String> = Vec::new();

    if let Some(last_value) = vector.last().map(|v| v.len().to_string()) {
        vector.push(last_value);
    }
}

你能解释一下您的第一个例子和原帖代码有何不同吗?它们之间具体的区别在哪里?Copy 在这里起到什么作用? - Jimmy
1
@JimmyCuadra 在 OP 的代码中,last_value()last_value 匹配。由于 last_value() 返回一个引用,因此 last_value 也成为了一个引用。然而,Francis 将模式更改为 &last_value,因此引用匹配了 &,而值本身则匹配了 last_value,因此被按值复制。这被称为解构。 - Malcolm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接