Recursive closures is possible in Rust, but it cannot mutate its captured variables as to make it recursive it have to keep a separate reference of the closure itself.
I am thinking a way to get around this restriction and make recursion easier. Like this:
let mut i1 = 1;
let mut i2 = 1;
let fib: &FnOnce(i:i32) -> i32 = &move |mut captured,i|
match i {
0 => i1,
1 => i2,
i => {
(captured.i1, captured.i2) =
(captured.i2, captured.i1 + captured.i2);
captured(i-1)
}
}
};
We make captured
work as self
in impl
blocks, and making it the closure itself. we can also allow it to be decorated like &captured
, &mut captured
etc. If captured
appears in the variables, it have to be the first one, and every access to the captured variable must have captured.
prefix.
Note: The move
keyword is not necessary here as in the above example we receive captured
by value, so it forces to move everything captured to the closure.
The move
keyword is still needed as in the above case we have option to copy
or move
. But move
will be redundant in cases when there are any captured variable is not Copy
.
Once we have this we may need to relax the Fn
trait structure to allow Fn
not necessary FnMut
and FnMut
not necessary FnOnce
, because once we use the above syntax, the result type will have only one Trait
being implemented, and it may not be an easy task to generate other variations for calling.
Of cause, for backward compatibility we will not change anything if the first variable of the closure is not captured
, and the result closure will have FnOnce
if only FnMut
is needed, FnMut
if only Fn
is needed, as usual. And there is no prefix when accessing captured variables.
Another benefit of this is it supports FnBox
the same way as others:
let mut i1 = 1;
let mut i2 = 1;
let fib: Box<FnBox(i:i32) -> i32> = box move |mut box captured,i|
match i {
0 => i1,
1 => i2,
i => {
(captured.i1, captured.i2) =
(captured.i2, captured.i1 + captured.i2);
captured(i-1)
}
}
};
Even when we later decided to make Box<FnOnce>
just work, stabilizing FnBox
is still a good option as it makes the above works the same way as the other variants. EDIT (Except that we might want to remove the closure grammar we use today being able to generate FnBox
automatically. This means if you still want to use FnBox
you have add box self
in the parameter list, explicitly.)
It is too early to formalize this into a RFC, I am just want to see how people feel about this.