reference - 为什么我不能从闭包中返回对外部变量的可变引用?

标签 reference rust closures lifetime mutable

当我遇到这个有趣的场景时,我正在玩 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();
}

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

强制闭包为 FnOnce 的另一种更明确的方法是将其传递给具有特征绑定(bind)的通用函数。此代码也可以正常工作:

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/52752259/

相关文章:

reference - 多个生命周期和移动 : assignment to borrowed `x` occurs here

rust - 匹配的“cannot borrow as mutable because it is also borrowed as immutable”

带闭包和 IIFE 的 JavaScript 计数器

c++ - 构造函数中的引用初始化问题

c++ - 指针和引用问题(链表)

c++ - 用于引用分配的运算符重载

rust - 使用新的异步/等待语法发送多组 HTTP 请求并控制工作人员数量的最快方法

rust - 当 `cargo install` 由于系统配置问题而失败时,如何避免重建依赖项?

ios - 扩展中的 Swift 闭包

ios - 持有对使用闭包的本地对象的强引用 [Swift]