如果向量为空,插入值时如何避免多次可变借用向量?
How to avoid multiple mutable borrows of a vector when inserting a value if the vector is empty?
在 this github discussion 中,您会发现这段引起借用检查器愤怒的代码:
fn main() {
let mut vec = vec!();
match vec.first() {
None => vec.push(5),
Some(v) => unreachable!(),
}
}
我理解为什么在不可变借用未解决的情况下进行突变是有问题的。我假设一个解决方案是明确地只借一个(一个可变的),但它仍然导致我有两个借用,一个不可变的借用,然后一个可变的借用:
fn main() {
let mut vec: Vec<i32> = vec!();
let r_vec: &mut Vec<i32> = &mut vec;
match r_vec.first() {
None => r_vec.push(5),
Some(v) => unreachable!(),
}
}
编译器仍然不高兴:
error[E0502]: cannot borrow `*r_vec` as mutable because it is also borrowed as immutable
--> testrust.rs:7:17
|
6 | match r_vec.first() {
| ----- immutable borrow occurs here
7 | None => r_vec.push(5),
| ^^^^^ mutable borrow occurs here
8 | Some(v) => unreachable!(),
9 | }
| - immutable borrow ends here
为什么我的解决方法不起作用,解决此问题的正确方法是什么?
你不知道。好吧,你 "avoid" 有多次借用......没有多次借用。
fn main() {
let mut vec = vec![];
if vec.first().is_none() {
vec.push(5);
}
}
更地道:
if vec.is_empty() {
vec.push(5);
}
在这两种情况下,我们借用 vec
来进行方法调用,但在执行 if
的主体之前终止借用。将其与 match
进行比较,其中在匹配头表达式中进行借用,然后与匹配臂共享。
take one mutable borrow that can be used for both cases
事情不是这样的。您必须了解记忆如何发挥作用以及参考是什么。 Vec
包含指向存储数据的内存的指针。
当您获得对数据向量的引用时,该引用保存数据的内存地址,并且编译器确保只有其中一个允许改变 Vec
。当你push
一个值时,那可能需要分配新的内存来存储所有的数据。这可能会使您持有的参考无效。如果发生这种情况,那么下次您使用该引用时,它会指向其他一些不相关的内存,您的程序会崩溃,您的用户数据会暴露于安全漏洞等,等等。
issue you linked and the related pre-RFC 的全部要点是此代码应该 能够被确定为安全的:
match vec.first() {
None => vec.push(5),
Some(v) => unreachable!(),
}
在这种情况下,程序员可以看到我们从不在 None
情况下使用借用,因此编译器理论上可以在执行任何匹配臂之前结束借用,或者以其他方式使两个臂关于生命周期不相交。它现在不这样做。
但是,在您的代码版本中,它实际上更糟。通过显式获取借用并将其保存在变量中,您可以延长借用需要保留的时间,迫使它们重叠。
目前,唯一的解决办法是重新排序代码以人为地限制借用。我在实践中并没有发现这很烦人,因为通常会有更好的代码组织方式。
另请参阅:
- match + RefCell = X does not live long enough
- 任何 90+ Rust questions that have the error message "mutable because it is also borrowed as immutable"
在 this github discussion 中,您会发现这段引起借用检查器愤怒的代码:
fn main() {
let mut vec = vec!();
match vec.first() {
None => vec.push(5),
Some(v) => unreachable!(),
}
}
我理解为什么在不可变借用未解决的情况下进行突变是有问题的。我假设一个解决方案是明确地只借一个(一个可变的),但它仍然导致我有两个借用,一个不可变的借用,然后一个可变的借用:
fn main() {
let mut vec: Vec<i32> = vec!();
let r_vec: &mut Vec<i32> = &mut vec;
match r_vec.first() {
None => r_vec.push(5),
Some(v) => unreachable!(),
}
}
编译器仍然不高兴:
error[E0502]: cannot borrow `*r_vec` as mutable because it is also borrowed as immutable
--> testrust.rs:7:17
|
6 | match r_vec.first() {
| ----- immutable borrow occurs here
7 | None => r_vec.push(5),
| ^^^^^ mutable borrow occurs here
8 | Some(v) => unreachable!(),
9 | }
| - immutable borrow ends here
为什么我的解决方法不起作用,解决此问题的正确方法是什么?
你不知道。好吧,你 "avoid" 有多次借用......没有多次借用。
fn main() {
let mut vec = vec![];
if vec.first().is_none() {
vec.push(5);
}
}
更地道:
if vec.is_empty() {
vec.push(5);
}
在这两种情况下,我们借用 vec
来进行方法调用,但在执行 if
的主体之前终止借用。将其与 match
进行比较,其中在匹配头表达式中进行借用,然后与匹配臂共享。
take one mutable borrow that can be used for both cases
事情不是这样的。您必须了解记忆如何发挥作用以及参考是什么。 Vec
包含指向存储数据的内存的指针。
当您获得对数据向量的引用时,该引用保存数据的内存地址,并且编译器确保只有其中一个允许改变 Vec
。当你push
一个值时,那可能需要分配新的内存来存储所有的数据。这可能会使您持有的参考无效。如果发生这种情况,那么下次您使用该引用时,它会指向其他一些不相关的内存,您的程序会崩溃,您的用户数据会暴露于安全漏洞等,等等。
issue you linked and the related pre-RFC 的全部要点是此代码应该 能够被确定为安全的:
match vec.first() {
None => vec.push(5),
Some(v) => unreachable!(),
}
在这种情况下,程序员可以看到我们从不在 None
情况下使用借用,因此编译器理论上可以在执行任何匹配臂之前结束借用,或者以其他方式使两个臂关于生命周期不相交。它现在不这样做。
但是,在您的代码版本中,它实际上更糟。通过显式获取借用并将其保存在变量中,您可以延长借用需要保留的时间,迫使它们重叠。
目前,唯一的解决办法是重新排序代码以人为地限制借用。我在实践中并没有发现这很烦人,因为通常会有更好的代码组织方式。
另请参阅:
- match + RefCell = X does not live long enough
- 任何 90+ Rust questions that have the error message "mutable because it is also borrowed as immutable"