![](/img/trans.png)
[英]“Cannot borrow immutable content as mutable” when moving a closure into a thread
[英]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
,则可以编译它。 如何解决这个问题? 我想在循环中调用匿名函数。
我们可以通过简单的不可变引用替换闭包来更简化您的示例
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
就像在你的例子中,用if
替换while
会使错误消失。 为什么?
您可能知道重要的Rust规则: 别名和可变性 。 它指出,在任何给定的时间,一个值可以被任意地多次借用或者可变地一次性地借用。
声明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
标记为“死”。 而这反过来意味着,行号中没有其他借用。 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();
使用那个闭包。 在循环的情况下,编译器无法缩小闭包的“活动时间”,以确保它不会与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
作为建议的其他答案,但这应该是最后的手段。
它并不完全确定你要完成什么,但解决这个问题的一种方法是使用std::cell::RefCell
( 在std和书中描述),而不是过于剧烈地改变你的代码。
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());
}
输出:
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]
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.