【问题标题】:Can I mutate a vector with a borrowed element?我可以用借来的元素对向量进行变异吗?
【发布时间】:2021-05-19 00:02:02
【问题描述】:

我正在尝试存储对可变向量元素的引用以供以后使用。但是,一旦我改变了向量,我就不能再使用存储的引用了。我知道这是因为借用对元素的引用也需要借用对向量本身的引用。因此,向量不能被修改,因为这将需要借用一个可变引用,当另一个对向量的引用已经被借用时,这是不允许的。

这是一个简单的例子

struct Person {
    name: String,
}

fn main() {
    // Create a mutable vector
    let mut people: Vec<Person> = ["Joe", "Shavawn", "Katie"]
        .iter()
        .map(|&s| Person {
            name: s.to_string(),
        })
        .collect();

    // Borrow a reference to an element
    let person_ref = &people[0];

    // Mutate the vector
    let new_person = Person {
        name: "Tim".to_string(),
    };
    people.push(new_person);

    // Attempt to use the borrowed reference
    assert!(person_ref.name == "Joe");
}

产生以下错误

error[E0502]: cannot borrow `people` as mutable because it is also borrowed as immutable
  --> src/main.rs:21:5
   |
15 |     let person_ref = &people[0];
   |                       ------ immutable borrow occurs here
...
21 |     people.push(new_person);
   |     ^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
...
24 |     assert!(person_ref.name == "Joe");
   |             --------------- immutable borrow later used here

我也尝试按照here 的建议对矢量元素进行装箱,但这无济于事。我认为它可能允许我删除对向量的引用,同时保持对元素的引用,但显然不是。

struct Person {
    name: String,
}

fn main() {
    // Create a mutable vector
    let mut people: Vec<Box<Person>> = ["Joe", "Shavawn", "Katie"]
        .iter()
        .map(|&s| {
            Box::new(Person {
                name: s.to_string(),
            })
        })
        .collect();

    // Borrow a reference to an element
    let person_ref = people[0].as_ref();

    // Mutate the vector
    let new_person = Box::new(Person {
        name: "Tim".to_string(),
    });
    people.push(new_person);

    // Attempt to use the borrowed reference
    assert!(person_ref.name == "Joe");
}

这仍然会产生同样的错误

error[E0502]: cannot borrow `people` as mutable because it is also borrowed as immutable
  --> src/main.rs:23:5
   |
17 |     let person_ref = people[0].as_ref();
   |                      ------ immutable borrow occurs here
...
23 |     people.push(new_person);
   |     ^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
...
26 |     assert!(person_ref.name == "Joe");
   |             --------------- immutable borrow later used here

有没有办法做到这一点,还是我试图做一些不可能的事情?

【问题讨论】:

  • 这是一个理论还是现实世界的例子?原因是,由于使用 assert!(people[0].name == "Joe"); 的选项被隐式丢弃,因此不清楚这个问题是理论上的,还是现实世界中的一个更大的图景阻止了提到的解决方案。
  • @Marcus - 我不确定你的意思是“使用...的选项被隐式丢弃”。这只是尝试访问people[0].name 的一种方式。这个问题确实来自一个真实世界的示例,该示例尝试在循环中填充可变向量,同时在哈希映射中存储对元素的引用。 Context.
  • 上下文澄清了一切 :) 我认为在这种情况下,没有比您的答案更好的解决方案了,因为一个参考可能超出范围。

标签: rust reference borrow-checker borrowing


【解决方案1】:

我发现使用reference counted smart pointer 可以让我完成我正在尝试的事情。共享所有权是必要的,因为否则如果原始向量超出范围,元素引用将变得无效(这将释放元素,无论是否有Box)。

以下代码编译成功。

use std::rc::Rc;

struct Person {
    name: String,
}

fn main() {
    // Create a mutable vector
    let mut people: Vec<Rc<Person>> = ["Joe", "Shavawn", "Katie"]
        .iter()
        .map(|&s| {
            Rc::new(Person {
                name: s.to_string(),
            })
        })
        .collect();

    // Borrow a reference to an element
    let person_ref = Rc::clone(&people[0]);

    // Mutate the vector
    let new_person = Rc::new(Person {
        name: "Tim".to_string(),
    });
    people.push(new_person);

    // Attempt to use the borrowed reference
    assert!(person_ref.name == "Joe");
}

如果其他人有任何更正、改进或进一步的见解,我很高兴听到。但如果不是,我现在对这个答案感到满意。

【讨论】:

  • "否则,如果原始向量超出范围,元素引用将变得无效"向量不能超出范围,问题是变异向量可以使引用完全无效.具体来说,push 会导致向量的大小调整,这可能需要将向量的内容 移动 到新的分配。因此,引用将变为 dangling
  • 那么你认为一种使用Box而不是Rc的方法吗?
  • 嗯,是的,也不是,在技术层面上,间接是存在的,但它会被拒绝,因为类型系统不理解它。因此,如果您绝对必须这样做,Rc 似乎是最不坏的方法。
  • 听起来你真的在说“只是不”。如果你知道一种方法,你能提供一个例子吗?
  • @Oliver 您可以使用unsafe 代码来实现,但这会混淆所有权并且不能很好地扩展。 Rust 中另一件常见的事情是将索引存储到向量中,而不是对其项的引用 (example)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-01
  • 2021-06-30
  • 1970-01-01
  • 2021-11-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多