当我遇到这个有趣的场景时,我正在玩 Rust 闭包:

fn main() {
    let mut y = 10;

    let f = || &mut y;

    f();
}

这给出了一个错误:

error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
 --> src/main.rs:4:16
  |
4 |     let f = || &mut y;
  |                ^^^^^^
  |
note: first, the lifetime cannot outlive the lifetime  as defined on the body at 4:13...
 --> src/main.rs:4:13
  |
4 |     let f = || &mut y;
  |             ^^^^^^^^^
note: ...so that closure can access `y`
 --> src/main.rs:4:16
  |
4 |     let f = || &mut y;
  |                ^^^^^^
note: but, the lifetime must be valid for the call at 6:5...
 --> src/main.rs:6:5
  |
6 |     f();
  |     ^^^
note: ...so type `&mut i32` of expression is valid during the expression
 --> src/main.rs:6:5
  |
6 |     f();
  |     ^^^

即使编译器试图逐行解释它,我仍然不明白它到底在提示什么。

是不是想说可变引用不能比封闭的闭包更长寿?

如果我删除调用 f() ,编译器不会提示。

最佳答案

精简版

闭包 f 存储对 y 的可变引用。如果允许返回此引用的副本,您最终会同时获得两个对 y 的可变引用(一个在闭包中,一个返回),这是 Rust 的内存安全规则所禁止的。

长版

闭包可以被认为是

struct __Closure<'a> {
    y: &'a mut i32,
}

因为它包含一个可变引用,所以闭包被称为 FnMut ,本质上与定义
fn call_mut(&mut self, args: ()) -> &'a mut i32 { self.y }

因为我们只有一个对闭包本身的可变引用,所以我们不能将字段 y 移出借用的上下文,我们也不能复制它,因为可变引用不是 Copy

我们可以通过强制将闭包调用为 FnOnce 而不是 FnMut 来欺骗编译器接受代码。这段代码工作正常:
fn main() {
    let x = String::new();
    let mut y: u32 = 10;
    let f = || {
        drop(x);
        &mut y
    };
    f();
}

由于我们在闭包范围内消费 x 并且 x 不是 Copy ,编译器检测到闭包只能是 FnOnce 。调用 FnOnce 闭包会按值传递闭包本身,因此我们可以将可变引用移出。

强制闭包为 FnOnce 的另一种更明确的方法是将其传递给具有 trait bound 的泛型函​​数。这段代码也能正常工作:
fn make_fn_once<'a, T, F: FnOnce() -> T>(f: F) -> F {
    f
}

fn main() {
    let mut y: u32 = 10;
    let f = make_fn_once(|| {
        &mut y
    });
    f();
}

关于reference - 为什么我不能从闭包返回对外部变量的可变引用?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57748424/

10-13 09:06