Reputation: 703
Is there a shorter way of writing these function signatures?
fn hof_five(a: i32, func: impl Fn(i32) -> i32) -> impl Fn(i32) -> i32 {
move |v| func(a + v)
}
fn hof_six(a: i32, func: Box<dyn Fn(i32) -> i32>) -> Box<dyn Fn(i32) -> i32> {
Box::new(move |v| func(a + v))
}
Something like using F: impl Fn(i32) -> i32 or F: Box<dyn Fn(i32) -> i32>
So that the signature becomes something like:
fn hof_five<F>(a: i32, func: F) -> F {
move |v| func(a + v)
}
Upvotes: 1
Views: 110
Reputation: 601529
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.
Upvotes: 2
Reputation: 430634
Not usefully, no, there is not.
To make something shorter, you generally have to reduce redundancy or remove characters.
impl
and fn
and i32
are already abbreviated, so they can't have characters removed.All that's left is textual similarities. You can use a macro to do that, but I don't think it's a good idea:
macro_rules! F {
() => (impl Fn(i32) -> i32);
}
fn hof_five(a: i32, func: F!()) -> F!() {
move |v| func(a + v)
}
See also:
You might also be interested in using where
clauses to spread out the complexity:
fn hof_five<F>(a: i32, func: F) -> impl Fn(i32) -> i32
where
F: Fn(i32) -> i32,
{
move |v| func(a + v)
}
Upvotes: 1