HYR*_*YRY 3 closures rust borrow-checker
这是代码:
fn test(){
let mut numbers = vec![2];
let f = || {
for _ in numbers.iter(){
}
false
};
while false {
let res = f();
if res {
numbers.push(10);
}
}
}
Run Code Online (Sandbox Code Playgroud)
错误是:
fn test(){
let mut numbers = vec![2];
let f = || {
for _ in numbers.iter(){
}
false
};
while false {
let res = f();
if res {
numbers.push(10);
}
}
}
Run Code Online (Sandbox Code Playgroud)
但是,如果我将while
关键字更改为if
,则可以对其进行编译。如何解决这个问题?我想在循环中调用匿名函数。
通过使用简单的不可变引用替换闭包,我们可以进一步简化您的示例
let mut numbers = vec![2];
let r = &numbers;
while false {
println!("{:?}", r);
numbers.push(10);
}
Run Code Online (Sandbox Code Playgroud)
在这里,我们得到这个错误:
error[E0502]: cannot borrow `numbers` as mutable because it is also borrowed as immutable
--> src/lib.rs:7:5
|
3 | let r = &numbers;
| -------- immutable borrow occurs here
...
6 | println!("{:?}", r); // use reference
| - immutable borrow later used here
7 | numbers.push(10);
| ^^^^^^^^^^^^^^^^ mutable borrow occurs here
Run Code Online (Sandbox Code Playgroud)
就像在您的示例中一样,将替换为while
可以if
使错误消失。为什么?
您可能知道重要的Rust规则:别名nand可变性。它指出,在任何给定时间,值可以借用一成不变任意多次或性情不定地一次。
该报表是临时可变numbers.push(10)
借入的numbers
(仅适用于该报表)。但是我们也有r
一个不变的参考。为了numbers.push(10)
工作,编译器必须确保当时没有其他借位存在。但是存在参考r
!此引用不能同时numbers.push(10)
存在。
让我们先来看这种if
情况:
let mut numbers = vec![2];
let r = &numbers; // <------+ (creation of r)
// |
if false { // |
println!("{:?}", r); // <------+ (last use of r)
numbers.push(10);
}
Run Code Online (Sandbox Code Playgroud)
虽然词法作用域意味着变量r
由于非词法生存期而仅在函数末尾删除,但编译器可以看到最后一次使用r
是println
在行中。然后,编译器可以r
在此行之后标记为“死”。反过来,这意味着该行中没有其他借方numbers.push(10)
,一切正常。
对于循环情况?让我们想象一下循环迭代三遍:
let mut numbers = vec![2];
let r = &numbers; // <------+ (creation of r)
// |
// First iteration // |
println!("{:?}", r); // |
numbers.push(10); // | <== oh oh!
// |
// Second iteration // |
println!("{:?}", r); // |
numbers.push(10); // |
// |
// Third iteration // |
println!("{:?}", r); // <------+ (last use of r)
numbers.push(10);
Run Code Online (Sandbox Code Playgroud)
从这里可以看出,r
活动时间重叠numbers.push(10)
(最后一个除外)。结果,编译器将产生错误,因为此代码违反了中央Rust规则。
对于您的闭包情况,其解释是相同的:闭包numbers
不可变地借用并f();
使用该闭包。在循环情况下,编译器无法充分缩短关闭的“有效时间”,以确保它不会与的可变借位重叠push
。
怎么修?
好吧,您可以numbers
每次进入闭包:
let mut numbers = vec![2];
let f = |numbers: &[i32]| {
for _ in numbers.iter(){
}
false
};
while false {
let res = f(&numbers);
if res {
numbers.push(10);
}
}
Run Code Online (Sandbox Code Playgroud)
之所以起作用,numbers
是因为现在,它也是为f(&numbers);
语句而临时借用的。
您也可以使用a RefCell
作为其他建议的答案,但这应该是最后的选择。