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



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

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

我想在名为holdersVec<_>中创建 10 个Holder秒。由于Holder引用Held结构,因此我还创建了一个名为heldvalsVec<_>,它将存储main函数作用域的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,
)

瓦尔格林德也没有显示内存泄漏或问题:

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(),这是个好主意吗?

不能借用为可变,因为它也被借用为不可变,其他类似的答案过于简单,无法解释循环和借用错误之间的关系。 此外,它们没有给出替代解决方案的指针。

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

从我上面的回复中复制:

这里的基本内容是,一旦您重新更改heldvalsholders就完全失效。因此,如果您完全填充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后对其进行更改。

最新更新