Can somebody help me to rewrite this piece of code with new unboxed closures:
struct Builder;
pub fn build(rules: |params: &mut Builder|) -> Builder {
let mut builder = Builder::new();
rules(&mut builder);
builder
}
I tried to write like this, but I got a lifetime error:
pub fn build<F>(rules: F) -> Builder where F: FnOnce<(&mut Builder,), ()> {
let mut builder = Builder::new();
rules(&mut builder);
builder
}
valico/src/builder.rs:48:59: 48:71 error: missing lifetime specifier [E0106]
valico/src/builder.rs:48 pub fn build<F>(rules: F) -> Builder where F: FnOnce<(&mut Builder,), ()> {
^~~~~~~~~~~~
What lifetime I need to specify? Simplified example in the sandbox.
This requires higher rank trait bounds, specifically, higher rank lifetimes. The full unsugared syntax would be F: for<'a> FnOnce<(&'a mut Builder,), ()>
.
Using a lifetime on the function can't work, e.g. if we had
pub fn build<'b, F>(rules: F) -> Builder where F: FnOnce<(&'b mut Builder,), ()>
This says that build
works with whatever lifetime the caller wishes (e.g. they could chose 'b
== 'static
), but this is invalid, because there is a specific concrete lifetime that needs to be the used: the lifetime of the &mut builder
inside the function. Using F: for<'a> ...
in a bound says that F
works with any lifetime 'a
, so the compiler sees that it is legal to substitute in the one of &mut builder
.
As I hinted above, that's the really ugly unsugared syntax. There's two successive ways this can be made much nicer. Firstly, the canonical way to use the closure traits is the ()
sugar: for<'a> FnOnce(&'a mut Builder) -> ()
, or, like with the rest of Rust, the -> ()
can be dropped: for<'a> FnOnce(&'a mut Builder)
. (NB. this is just sugar for FnOnce<...>
, but only the sugared syntax will be stabilised for interacting with these traits at 1.0.)
Then, the paren syntax has a little extra rule: it automatically inserts lifetimes that act like for<'a>
(specifically, it undergoes lifetime elision with any inserted lifetime placed into a for
on the trait), so just F: FnOnce(&mut Builder)
is equivalent to F: for<'a> FnOnce(&'a mut Builder)
, and it's the recommended version.
Applying these fixes to your playpen example:
pub fn initialize_with_closure<F>(rules: F) -> uint where F: FnOnce(&mut uint) {
let mut i = 0;
rules(&mut i);
i
}
// equivalently
pub fn initialize_with_closure_explicit<F>(rules: F) -> uint where F: for<'a> FnOnce(&'a mut uint) -> () {
let mut i = 0;
rules(&mut i);
i
}
pub fn main() {
initialize_with_closure(|i: &mut uint| *i = *i + 20);
initialize_with_closure_explicit(|i: &mut uint| *i = *i + 20);
}
playpen
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With