rustclosureshigher-order-functions

How to create a sized closure or implement Fn/FnMut/FnOnce on a struct?


Basically, I want to write a function that returns a closure. How can I do this without having to return Box<FnOnce(u32)>?

From the closures chapter of the rust book, I read that a closure is just syntactic sugar for a struct and an impl of FnOnce. Here is my attempt:

#[derive(Debug)]
struct MyError {
    code: u32,
    location: &'static str,
}
// Here is my closure:
struct MyErrorPartial {
    location: &'static str,
}
impl FnOnce(u32) for MyErrorPartial {
    type Output = MyError;

    fn call_once(self, args: u32) -> MyError {
        MyError {
            code: args,
            location: self.location,
        }
    }
}
fn error_at(location: &'static str) -> MyErrorPartial {
    MyErrorPartial {location: location}
}

fn function_returning_code() -> Result<(), u32> {
    Err(123)
}
fn function_with_error() -> Result<(), MyError> {
    try!(function_returning_code().map_err(error_at("line1")));
    try!(function_returning_code().map_err(error_at("line2")));
    Ok(())
}
fn main() {
    function_with_error().unwrap();
}

It currently gives an error:

<anon>:11:12: 11:17 error: associated type bindings are not allowed here [E0229]
<anon>:11 impl FnOnce(u32) for MyErrorPartial {
                     ^~~~~

Solution

  • The syntax for manually implementing a Fn* trait on a struct is this one:

    impl FnOnce<(Arg1,Arg2,Arg3,)> for MyStruct {
        type Output = MyOutput;
        extern "rust-call" fn call_once(args: (Arg1, Arg2, Arg3,)) -> MyOutput {
           // implementation here
        }
    }
    

    Note that all the arguments are given as a single tuple.

    Also, this syntax is unstable and require #![feature(core, unboxed_closures)], thus you cannot use it on the beta channel, only nightlies.

    In you case, it would translate like this:

    impl FnOnce<(u32,)> for MyErrorPartial {
        type Output = MyError;
    
        extern "rust-call" fn call_once(self, args: (u32,)) -> MyError {
            MyError {
                code: args.0,
                location: self.location,
            }
        }
    }