我可以用借来的元素来改变向量吗?

问题描述 投票:0回答:3

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

这是一个简单的例子

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

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

rust reference borrow-checker borrowing
3个回答
2
投票

我发现使用引用计数智能指针可以让我完成我正在尝试的事情。共享所有权是必要的,这是有道理的,因为否则,如果原始向量超出范围,元素引用将变得无效(这将释放元素,无论有或没有

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");
}

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


2
投票

虽然奥利弗给出的答案似乎是最好的解决方案,

但是如果我们只想改变向量(如问题标题所述),即不再推送任何元素(因为向量处理复杂),只需修改值,我们可以使用 Vec 给出的函数 split_at_mut() .

let (head, tail) = people.split_at_mut(1);

现在我们可以在变异尾部的同时让某些东西借用头部。但我们无法推动它,因为头部和尾部都是切片。


0
投票

如果您只按原样使用,

您只需将

let person_ref = &people[0];
移至
assert!(person_ref.name == "Joe");

上方即可
© www.soinside.com 2019 - 2024. All rights reserved.