One frequently requested feature is to infer the move
keyword in closures. This is not always possible – to infer it with pure fidelity would require knowledge of the complete set of region constraints, and we can’t know the complete set of region constraints until we decide whether a closure is escaping (move
) or by-reference. However, in some circumstances, notably when we see a 'static
constraint, we could infer that move
is required. (Note though that the move
keyword is currently required more often than it ought to be. Once PR 21604 lands, the move
keyword should only be required for closures that actually escape the current stack frame, either because they serve as a thread body or because they are returned to a caller.)
This would be simple to implement, but something about the idea of inferring move
has always made me uncomfortable. This is because escaping closures feel like a different semantic entity to me, so it seems strange that we should even want to infer it. Today I came up with a good example to highlight what I mean. Consider this closure, which increments the upvar i
from the surrounding environment:
fn foo() {
let mut i = 0;
bar(|| i += 1);
println!("i={}", i); // Prints what?
}
If this is an escaping closure, then what is happening is that the closure copies i
into its environment and increments the result there. This means that the final line will always print 0
, because the closure is operating on a separate copy of i
. If this is a non-escaping closure, then the closure will mutate i
from its surrounding environment, and hence the final line will print 1
(assuming the closure is called once).
Now, in the compiler as it exists today, this distinction is syntactically clear. There is no keyword move
, so the closure above is a non-escaping closure, and hence we expect it to mutate i
in place. However, if we start inferring the move
keyword, then the example above is ambiguous, and we would have to look at the definition of bar
to determine what is happening – moreover, it may not be entirely clear, because the inference will be something of a heuristic. The inference I proposed (using 'static) is backwards compatible in that it doesn’t cause any program that currently executes to behave differently. But it will cause programs that would fail to compile today, because they need a move
, to compile – and in so doing it may change the semantics from what you expect.
I’m not sure what’s the best way to proceed here: implement the inference, or close #18799 as “WON’T FIX”.