Reputation: 2147
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?
Upvotes: 9
Views: 4150
Reputation: 1200
There is now a solution for this, using the nightly feature type_alias_impl_trait
, as written in this nice post (that then goes on to automate currying in a proc macro): https://peppe.rs/posts/auto-currying_rust_functions/#refinement
Adapting the method from the post to the code from the question we get:
#![feature(type_alias_impl_trait)] // allows us to use `impl Fn` in type aliases!
type T0 = i32; // the return value when zero args are to be applied
type T1 = impl Fn(i32) -> T0; // the return value when one arg is to be applied
type T2 = impl Fn(i32) -> T1; // the return value when two args are to be applied
fn add(x: i32) -> T2 {
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));
}
You can play with this code in the playground
Upvotes: 1
Reputation: 15085
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 Trait
s 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"
}
See also:
Upvotes: 9