Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to rewrite code to new unboxed closures

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.

like image 676
Stanislav Panferov Avatar asked Jan 07 '15 08:01

Stanislav Panferov


1 Answers

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

like image 141
huon Avatar answered Oct 24 '22 08:10

huon