I would like to be able to implement a trait for different function signatures, but the compiler gives me strange errors. I have tried to reduce the problem in the following minimal example:
trait MyTrait<P> {
fn trait_method(&self, p: P);
}
impl MyTrait<fn(u32)> for MyStruct {
fn trait_method(&self, p: fn(u32)) {
todo!()
}
}
impl MyTrait<fn(u64)> for MyStruct {
fn trait_method(&self, p: fn(u64)) {
todo!()
}
}
struct MyStruct;
fn test() {
let my_struct = MyStruct;
// Gives error:
// the trait bound `MyStruct: MyTrait<fn(u32) {test_fun}>` is not satisfied
// the following implementations were found:
// <MyStruct as MyTrait<fn(u32)>>
// <MyStruct as MyTrait<fn(u64)>>
my_struct.trait_method(test_fun);
// Is ok
let fun: fn(u32) = test_fun;
my_struct.trait_method(fun);
// Gives error
// the trait bound `MyStruct: MyTrait<[closure@src/main.rs:176:28: 176:39]>` is not satisfied
// the following implementations were found:
// <MyStruct as MyTrait<fn(u32)>>
// <MyStruct as MyTrait<fn(u64)>>
my_struct.trait_method(|hi: u32|{});
// Is ok
let fun: fn(u32) = |hi: u32|{};
my_struct.trait_method(fun);
}
fn test_fun(hi: u32) {}
If I remove the impl MyTrait<fn(u64)>
block, then the problem just dissapears, and I can use either way of calling the Trait function. Only when I have multiple implementations of the same trait do these errors occur.
Why is this Rust code incorrect, and what would be a way in which I could fix this? It is quite important to me that it is callable without an intermediate assignmment, since this trait will end up in the public API.
Thanks in advance
edit: The following code works as well:
my_struct.trait_method(test_fun as fn(u32));
Okay I managed to find a way around this problem:
pub trait VarFunc<A, P, R, T> {}
impl<F, A: Actor, P, R> VarFunc<A, P, R, WithState> for F where
F: Fn(&mut A, &mut State<A>, P) -> Flow<A, R>
{
}
impl<F, A: Actor, P, R> VarFunc<A, P, R, WithoutState> for F where
F: Fn(&mut A, P) -> Flow<A, R>
{
}
pub struct WithState;
pub struct WithoutState;
Basically, I have created a new trait VarFunc
, which can be implemented both WithState
and WithoutState
. Then, when specifying the Trait impl, instead of using F: Fn(X) -> Y
, we use F: VarFunc<StateX/StateY>
, which can actually be seen as different by the compiler.