Short version
The closure f
stores a mutable reference to y
. If it were allowed to return a copy of this reference, you would end up with two simultaneous mutable references to y
(one in the closure, one returned), which is forbidden by Rust's memory safety rules.
Long version
The closure can be thought of as
struct __Closure<'a> {
y: &'a mut i32,
}
Since it contains a mutable reference, the closure is called as FnMut
, essentially with the definition
fn call_mut(&mut self, args: ()) -> &'a mut i32 { self.y }
Since we only have a mutable reference to the closure itself, we can't move the field y
out of the borrowed context, neither are we able to copy it, since mutable references aren't Copy
.
We can trick the compiler into accepting the code by forcing the closure to be called as FnOnce
instead of FnMut
. This code works fine:
fn main() {
let x = String::new();
let mut y: u32 = 10;
let f = || {
drop(x);
&mut y
};
f();
}
Since we are consuming x
inside the scope of the closure and x
is not Copy
, the compiler detects that the closure can only be FnOnce
. Calling an FnOnce
closure passes the closure itself by value, so we are allowed to move the mutable reference out.
Another more explicit way to force the closure to be FnOnce
is to pass it to a generic function with a trait bound. This code works fine as well:
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();
}
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…