繁体   English   中英

在调用一个借用为不可变的闭包时,不能在循环中借用可变的东西吗?

[英]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规则。

对于你的闭包案例,解释是相同的:闭包不可分割地借用numbersf(); 使用那个闭包。 在循环的情况下,编译器无法缩小闭包的“活动时间”,以确保它不会与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] 

Rust Playground演示

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM