functionrustfunctional-programmingclosurescurrying

How to implement a multi-level currying function in Rust?


I attempt to implement a currying function similar to Functional Programming Jargon in Rust:

fn add_origin(x: i32) -> impl Fn(i32) -> i32 {
    return move |y| {
        x + y
    };
}

fn main() {
    let add5 = add_origin(5);
    println!("Call closure: {}", add5(6));
}

This works, but if I add one level deeper:

fn add(x: i32) -> impl Fn(i32) -> impl Fn(i32) -> i32 {
    return move |y: i32| {
        return move |z: i32| {
            x + y + z
        }
    };
}

fn main() {
    let add5 = add(5);
    let add5_10 = add5(10);
    println!("Call closure: {}", add5_10(6));
}

Compiler does not accept and tells me:

error[E0562]: `impl Trait` not allowed outside of function and inherent method return types
 --> src/main.rs:7:35
  |
7 | fn add(x: i32) -> impl Fn(i32) -> impl Fn(i32) -> i32 {
  |                                   ^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

Why is this not allowed? Is there a better alternative in Rust?


Solution

  • impl Trait syntax can only be used in argument position or return position of a function signature. Both of these are fine:

    fn takes_fn(fn_arg: impl Fn(i32) -> i32) {}
    
    fn returns_fn() -> impl Fn(i32) -> i32 {
      |x| x
    }
    

    However neither of these work:

    fn takes_fn(fn_arg: impl Fn(i32) -> impl Fn(i32) -> i32) {}
    
    fn returns_fn() -> impl Fn(i32) -> impl Fn(i32) -> i32 {
      |x| x
    }
    

    Because the second nested impl Trait is no longer in argument or return position but is now part of the argument or return type's signature, which is not somewhere where it is allowed. Basically, multiple nested impl Traits will never work regardless of where you place them and you'll always get the same compile error.

    The good news is this doesn't stop you from accomplishing what you want to accomplish because impl Trait is just syntax sugar and its use is optional. In your particular example we can use a boxed trait object to return your curried function.

    fn add(x: i32) -> impl Fn(i32) -> Box<dyn Fn(i32) -> i32> {
        move |y: i32| {
            Box::new(move |z: i32| {
                x + y + z
            })
        }
    }
    
    fn main() {
        let add5 = add(5);
        let add5_10 = add5(10);
        println!("Call closure: {}", add5_10(6)); // prints "Call closure: 21"
    }
    

    playground

    See also: