调用一个借用为不可变的闭包时,不能在循环中借用为可变的吗?
Cannot borrow as mutable in a loop when calling a closure that borrows as immutable?
代码如下:
fn test(){
let mut numbers = vec![2];
let f = || {
for _ in numbers.iter(){
}
false
};
while false {
let res = f();
if res {
numbers.push(10);
}
}
}
错误是:
|
15 | let f = || {
| -- immutable borrow occurs here
16 | for _ in numbers.iter(){
| ------- first borrow occurs due to use of `numbers` in closure
...
22 | let res = f();
| - immutable borrow later used here
23 | if res {
24 | numbers.push(10);
| ^^^^^^^^^^^^^^^^ mutable borrow occurs here
但是如果我把while
关键字改成if
就可以编译了。如何解决这个问题?我想在循环中调用匿名函数。
不确定您要完成什么,但解决此问题的一种方法是使用 std::cell::RefCell
(描述 in the std and in the book):
use std::cell::RefCell;
fn test(){
let numbers = RefCell::new(vec![2]);
let f = || {
for _ in numbers.borrow().iter(){
}
false
};
while false {
let res = f();
if res {
numbers.borrow_mut().push(10);
}
}
}
这里有一个稍微调整过的演示,它实际上做了一些事情:
use std::cell::RefCell;
fn main() {
test();
}
fn test() {
let numbers = RefCell::new(vec![0]);
let f = || {
for n in numbers.borrow().iter() {
println!("In closure: {}", n);
}
println!();
true
};
let mut i = 1;
while i <= 3 {
let res = f();
if res {
numbers.borrow_mut().push(i);
}
i += 1;
}
println!("End of test(): {:?}", numbers.borrow());
}
Output:
In closure: 0
In closure: 0
In closure: 1
In closure: 0
In closure: 1
In closure: 2
End of test(): [0, 1, 2, 3]
我们可以通过用简单的不可变引用替换闭包来进一步简化您的示例
let mut numbers = vec![2];
let r = &numbers;
while false {
println!("{:?}", r);
numbers.push(10);
}
这里我们得到这个错误:
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
就像您的示例一样,将 while
替换为 if
会使错误消失。为什么?
您可能知道重要的 Rust 规则:Aliasing nand mutability。它指出,在任何给定时间,一个值可以不可变地任意多次借用 或 只可变地借用一次。
语句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);
}
虽然词法范围意味着变量 r
仅在函数末尾被丢弃,但由于非词法生命周期,编译器可以看到最后一次使用 r
是在println
行。然后编译器可以在这一行之后将 r
标记为 "dead"。这反过来意味着,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);
从这里可以看出,r
活跃的时间与numbers.push(10)
重叠(最后一个除外)。结果,编译器将产生一个错误,因为这段代码违反了 Rust 的中心规则。
对于闭包情况的解释是相同的:闭包不可变地借用 numbers
并且 f();
使用该闭包。在循环情况下,编译器无法充分收缩闭包的 "alive time" 以确保它不会与 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);
}
}
这是有效的,因为现在,numbers
被不变地借用,也只是暂时用于 f(&numbers);
语句。
您也可以使用 RefCell
作为其他答案的建议,但这应该是最后的选择。
代码如下:
fn test(){
let mut numbers = vec![2];
let f = || {
for _ in numbers.iter(){
}
false
};
while false {
let res = f();
if res {
numbers.push(10);
}
}
}
错误是:
|
15 | let f = || {
| -- immutable borrow occurs here
16 | for _ in numbers.iter(){
| ------- first borrow occurs due to use of `numbers` in closure
...
22 | let res = f();
| - immutable borrow later used here
23 | if res {
24 | numbers.push(10);
| ^^^^^^^^^^^^^^^^ mutable borrow occurs here
但是如果我把while
关键字改成if
就可以编译了。如何解决这个问题?我想在循环中调用匿名函数。
不确定您要完成什么,但解决此问题的一种方法是使用 std::cell::RefCell
(描述 in the std and in the book):
use std::cell::RefCell;
fn test(){
let numbers = RefCell::new(vec![2]);
let f = || {
for _ in numbers.borrow().iter(){
}
false
};
while false {
let res = f();
if res {
numbers.borrow_mut().push(10);
}
}
}
这里有一个稍微调整过的演示,它实际上做了一些事情:
use std::cell::RefCell;
fn main() {
test();
}
fn test() {
let numbers = RefCell::new(vec![0]);
let f = || {
for n in numbers.borrow().iter() {
println!("In closure: {}", n);
}
println!();
true
};
let mut i = 1;
while i <= 3 {
let res = f();
if res {
numbers.borrow_mut().push(i);
}
i += 1;
}
println!("End of test(): {:?}", numbers.borrow());
}
Output:
In closure: 0 In closure: 0 In closure: 1 In closure: 0 In closure: 1 In closure: 2 End of test(): [0, 1, 2, 3]
我们可以通过用简单的不可变引用替换闭包来进一步简化您的示例
let mut numbers = vec![2];
let r = &numbers;
while false {
println!("{:?}", r);
numbers.push(10);
}
这里我们得到这个错误:
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
就像您的示例一样,将 while
替换为 if
会使错误消失。为什么?
您可能知道重要的 Rust 规则:Aliasing nand mutability。它指出,在任何给定时间,一个值可以不可变地任意多次借用 或 只可变地借用一次。
语句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);
}
虽然词法范围意味着变量 r
仅在函数末尾被丢弃,但由于非词法生命周期,编译器可以看到最后一次使用 r
是在println
行。然后编译器可以在这一行之后将 r
标记为 "dead"。这反过来意味着,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);
从这里可以看出,r
活跃的时间与numbers.push(10)
重叠(最后一个除外)。结果,编译器将产生一个错误,因为这段代码违反了 Rust 的中心规则。
对于闭包情况的解释是相同的:闭包不可变地借用 numbers
并且 f();
使用该闭包。在循环情况下,编译器无法充分收缩闭包的 "alive time" 以确保它不会与 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);
}
}
这是有效的,因为现在,numbers
被不变地借用,也只是暂时用于 f(&numbers);
语句。
您也可以使用 RefCell
作为其他答案的建议,但这应该是最后的选择。