The two examples you gave are quite different.
fn hof_five(a: i32, func: impl Fn(i32) -> i32) -> impl Fn(i32) -> i32 {
move |v| func(a + v)
}
This function accepts a closure of a type implementing Fn(i32) -> i32
, and returns a closure of a type implementing this trait, but the argument and return types are different types. The argument type is inferred by the compiler based on the closure you pass in, and the return type is inferred based on the closure you return. Each closure has its own individual type.
Since argument type and return type are different types, there is no way to refer to them by the same name. The only thing you could do is define a trait that requires Fn(i32) -> i32
as a prerequisite:
trait MyFn: Fn(i32) -> i32 {}
impl<T> MyFn for T
where
T: Fn(i32) -> i32,
{}
With this trait definition, you can rewrite hof_five
as
fn hof_five(a: i32, func: impl MyFn) -> impl MyFn {
move |v| func(a + v)
}
The blanket implementation of MyFn
makes sure that all closures implementing Fn(i32) -> i32
automatically also implement MyFn
.
fn hof_six(a: i32, func: Box<dyn Fn(i32) -> i32>) -> Box<dyn Fn(i32) -> i32> {
Box::new(move |v| func(a + v))
}
This function accepts a pointer pointing to a closure implementing Fn(i32) -> i32
, but the concrete type of that closure is not known at compile time. If you actually call the closure, the concrete type of the closure is determined at runtime, and the generated code dynamically dispatches to the right function. In this case, argument type and return type are the same type, so you can use a type alias if you want to:
type MyBoxedFn = Box<dyn Fn(i32) -> i32>;
fn hof_six(a: i32, func: MyBoxedFn) -> MyBoxedFn {
Box::new(move |v| func(a + v))
}
This is completely equivalent to the original version.