The problem is the possibility to call the closure twice. On the first run, the captured variable num
is moved into inner
, that is, moved out of the closure's environment. Then, on the second call, the place where num
was is now invalid (since it was moved out), which breaks memory safety.
In more detail, one can regard closures as (approximately)
struct Closure { // stores all the captured variables
num: ~int
}
impl Closure {
fn call(&self, x: int) -> int {
// valid:
*self.num + x
// invalid:
// *inner(self.num) + x
}
}
Hopefully this makes it clearer: in the invalid one, one is trying to move self.num
out from behind a borrowed pointer into the inner
call (after which it is entirely disconnected from the num
field). If this were possible, then self
would be left in an invalid state, since, e.g., the destructor on self.num
may've have been called which frees the memory (violating memory safety).
One resolution of this is "once functions", which are implemented but are hidden behind a compiler flag, since they may be removed in favour of (at it's most basic) adjusting the type of call
above to be fn call(self, x: int)
, that is, calling the closure moves self
which means you can then move out of the environment (since call
then owns self
and its fields) and also means that the function is statically guaranteed to be called only once*.
*Not strictly true if the closure's environment doesn't move ownership, e.g. if it was struct Env { x: int }
.