当我遇到这个有趣的场景时,我正在玩 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
的另一种更明确的方法是将其传递给具有特征绑定(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/