|| unboxed closure form should be split into two forms—
|| for nonescaping closures and
move || for escaping closures—and the capture clauses and self type specifiers should be removed.
Having to specify
ref and the capture mode for each unboxed closure is inconvenient (see Rust PR rust-lang/rust#16610). It would be more convenient for the programmer if the type of the closure and the modes of the upvars could be inferred. This also eliminates the "line-noise" syntaxes like
|&:|, which are arguably unsightly.
Not all knobs can be removed, however—the programmer must manually specify whether each closure is escaping or nonescaping. To see this, observe that no sensible default for the closure
|| (*x).clone() exists: if the function is nonescaping, it's a closure that returns a copy of
x every time but does not move
x into it; if the function is escaping, it's a closure that returns a copy of
x and takes ownership of
Therefore, we need two forms: one for nonescaping closures and one for escaping closures. Nonescaping closures are the commonest, so they get the
|| syntax that we have today, and a new
move || syntax will be introduced for escaping closures.
For unboxed closures specified with
||, the capture modes of the free variables are determined as follows:
Any variable which is closed over and borrowed mutably is by-reference and mutably borrowed.
Any variable of a type that does not implement
Copywhich is moved within the closure is captured by value.
Any other variable which is closed over is by-reference and immutably borrowed.
The trait that the unboxed closure implements is
FnOnce if any variables were moved out of the closure; otherwise
FnMut if there are any variables that are closed over and mutably borrowed; otherwise
ref prefix for unboxed closures is removed, since it is now essentially implied.
We introduce a new grammar production,
move ||. The value returned by a
move || implements
Fn, as determined above; thus, for example,
move |x: int, y| x + y produces an unboxed closure that implements the
Fn(int, int) -> int trait (and thus the
FnOnce(int, int) -> int trait by inheritance). Free variables referenced by a
move || closure are always captured by value.
In the trait reference grammar, we will change the
|&:| sugar to
|&mut:| sugar to
FnMut(), and the
|:| sugar to
FnOnce(). Thus what was before written
fn foo<F:|&mut: int, int| -> int>() will be
fn foo<F:FnMut(int, int) -> int>().
It is important to note that the trait reference syntax and closure construction syntax are purposefully distinct. This is because either the
|| form or the
move || form can construct any of
Having two syntaxes for closures could be seen as unfortunate.
movebecomes a keyword.
Keep the status quo:
|&:|are the only ways to create unboxed closures, and
refmust be used to get by-reference upvars.
Use some syntax other than
move ||for escaping closures.
|&:|syntax only for trait reference sugar.
fn()syntax for trait reference sugar.
There may be unforeseen complications in doing the inference.