I'm trying to write code which is generic over an operation, but also has convenience functions for commonly used ones, like addition, which I want to define for all types T: Add
.
This works fine if I define standalone functions since I can use impl Trait
in the return value to hide the opaque type.
However, if I want to define this function inside a trait, I am forced to name the type of <T as Add>::add
. How can I do this?
use std::ops::Add;
struct Operation<T, F>
where
F: Fn(T, T) -> T,
{
arg: T,
f: F,
}
fn addition<T>(arg: T) -> Operation<T, impl Fn(T, T) -> T>
where
T: Add<Output = T>,
{
Operation { arg, f: T::add }
}
trait Addable<T>
where
T: Add<Output = T>,
{
fn addition(self) -> Operation<T, impl Fn(T, T) -> T>;
//fn addition(self) -> Operation<T, ???>;
}
Error:
error[E0562]: `impl Trait` not allowed outside of function and inherent method return types
--> src/main.rs:22:39
|
22 | fn addition(self) -> Operation<T, impl Fn(T, T) -> T>;
| ^^^^^^^^^^^^^^^^^^
The reason impl Trait
isn't allowed in the return position of trait methods is because there can be multiple implementations of the method, whereas impl Trait
is only valid if there's a single implementation of the function or method.
One option would be to use a boxed trait object closure:
trait Addable<T>
where
T: Add<Output = T>,
{
fn addition(self) -> Operation<T, Box<dyn Fn(T, T) -> T>>;
}
However, since it seems unlikely that the closure would ever need to capture variables from its environment you can use a plain function pointer instead. This saves you an unnecessary heap allocation. Here's a simple i32
example using a plain function pointer in the trait method return type:
trait Addable<T>
where
T: Add<Output = T>,
{
fn addition(self) -> Operation<T, fn(T, T) -> T>;
}
struct SomeType {
arg: i32,
}
impl Addable<i32> for SomeType {
fn addition(self) -> Operation<i32, fn(i32, i32) -> i32> {
Operation {
arg: self.arg,
f: i32::add,
}
}
}
Here's a generic example using plain function pointer in the trait method return type:
trait Addable<T>
where
T: Add<Output = T>,
{
fn addition(self) -> Operation<T, fn(T, T) -> T>;
}
struct SomeType<T> {
arg: T,
}
impl<T> Addable<T> for SomeType<T>
where T: Add + Add<Output = T>
{
fn addition(self) -> Operation<T, fn(T, T) -> T> {
Operation {
arg: self.arg,
f: T::add,
}
}
}