Jasper van der Werf
Jasper van der Werf

Reputation: 123

Rust trait bounds not satisfied. Either intermediate assignment is necessary, or removal of a Trait implementation

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));

Upvotes: 3

Views: 2875

Answers (2)

Jasper van der Werf
Jasper van der Werf

Reputation: 123

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.

Upvotes: 2

at54321
at54321

Reputation: 11796

Let's compare this:

let fun: fn(u32) = test_fun;

and this:

let fun = test_fun;

The type of fun in the first example is explicitly set to fn(u32), whereas in the second version it's actually fn(u32) {test_fun}. Those are two different types. The first one is a function pointer. The second one is a function item. When you explicitly specify the type of fun, what happens is known as coercion.

Upvotes: 0

Related Questions