为什么当我保留对附加到循环内的向量中的元素的引用时,借用检查器会引发错误?

Why does the borrow checker raise an error when I keep a reference to an element in a vector that is appended to within a loop?

我有两个结构,HolderHeldHolder 持有对 Held 的引用。 Held 持有 i32:

struct Holder<'a> {
    val: &'a Held,
}

#[derive(Debug)]
struct Held(i32);

我想在名为 holdersVec<_> 中创建 10 个 Holder。由于 Holder 引用了 Held 结构,我还创建了一个名为 heldvalsVec<_> 来存储 [=30= 范围内的 Held 结构] 函数:

pub fn main() {
    // contains the `Holder`s
    let mut holders = vec![];

    // contains the `Held`s
    let mut heldvals = vec![];

    for i in 0..10 {
        heldvals.push(Held(i));

        holders.push(Holder {
            val: &heldvals.last().unwrap(),
        });
    }
}

当我试图编译这个程序时,我得到一个错误:

error[E0502]: cannot borrow `heldvals` as mutable because it is also borrowed as immutable
   |
   |         heldvals.push(Held(i));
   |         ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
   | 
   |         holders.push(Holder {
   |         ------- immutable borrow later used here
   |             val: &heldvals.last().unwrap(),
   |                   -------- immutable borrow occurs here

作为一种解决方法,我不情愿地决定使用 unsafe,它可以正常工作而没有任何错误。我什至实现了 Drop 特性来确认没有内存问题。

// ...
impl Drop for Held {
    fn drop(&mut self) {
        dbg!(self);
    }
}

pub fn main() {
    let mut holders = vec![];

    let mut heldvals = vec![];
    let hptr = &mut heldvals as *mut Vec<Held>;

    for i in 0..10 {
        println!("creation");
        unsafe {
            (*hptr).push(Held(i));
        }

        holders.push(Holder {
            val: &heldvals.last().unwrap(),
        });
        println!("replacement");
    }
}

运行 上面的代码给出了这个(减少的)输出:

creation
replacement (10 times)
[src/main.rs:12] self = Held(
    0,
)
... 
[src/main.rs:12] self = Held(
    9,
)

Valgrind 未显示内存泄漏或问题:

HEAP SUMMARY:
    in use at exit: 0 bytes in 0 blocks
  total heap usage: 18 allocs, 18 frees, 3,521 bytes allocated

All heap blocks were freed -- no leaks are possible

ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

有没有办法避免使用unsafe?我发现了 Vec::reserve(),这是个好主意吗?

和其他类似的答案过于简单,没有解释循环和借用错误之间的关系。此外,他们没有给出替代解决方案的指针。

引用计数器的使用对我来说是不可能的。我想要一个简单的方法来保存引用直到程序退出。

从我上面的回复中复制:

这里的基本情况是,一旦你重新更改 heldvals 那么 holders 就完全失效了。因此,如果您完全填充 heldvals,然后遍历它以填充 holders,那么就可以了。但是一旦你再次改变heldvalsholders就失效了

#[derive(Debug)]
struct Holder<'a> {
    val: &'a Held,
}

#[derive(Debug)]
struct Held(i32);

pub fn main() {
    // contains the `Holder`s
    let mut holders = vec![];

    // contains the `Held`s
    let mut heldvals = vec![];

    for i in 0..10 {
        heldvals.push(Held(i));
    }
    for i in 0..10 {
        holders.push(Holder {
            val: &heldvals[i],
        });
    }
    for cur in holders.iter()
    {
        println!("cur: {:?}", cur);
    }
    // invalidate the holders
    heldvals.push(Held(15));
    println!("holders[1]: {:?}", holders[1])
}

那将不会编译。最后两行给出了这个错误:

   Compiling playground v0.0.1 (/playground)
error[E0502]: cannot borrow `heldvals` as mutable because it is also borrowed as immutable
  --> src/main.rs:29:5
   |
21 |             val: &heldvals[i],
   |                   -------- immutable borrow occurs here
...
29 |     heldvals.push(Held(15));
   |     ^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
30 |     println!("holders[1]: {:?}", holders[1])
   |                                  ------- immutable borrow later used here

For more information about this error, try `rustc --explain E0502`.

但是删除那些行,它起作用了:

cur: Holder { val: Held(0) }
cur: Holder { val: Held(1) }
cur: Holder { val: Held(2) }
cur: Holder { val: Held(3) }
cur: Holder { val: Held(4) }
cur: Holder { val: Held(5) }
cur: Holder { val: Held(6) }
cur: Holder { val: Held(7) }
cur: Holder { val: Held(8) }
cur: Holder { val: Held(9) }

如上,这是因为你有两个可变变量,一旦你从第一个变量中借用,你就不能改变它,直到借用被“归还”。一旦你从第一个向量中借用了一些东西(或许多东西),你实际上是在“锁定”第一个向量。您不能同时构建 heldvalsholders,也不能在借用后更改 heldvals