0

I'd like to invoke a function pointer which itself takes a trait object as an argument:

fn invoke(x: &Fn(&WithFoo), y: &MyStruct) {
    // MyStruct implements WithFoo
    x(y);
}

So far so good. Now, what I'm having trouble with is how to call this with a function pointer with signature such as &Fn(&WithFooBar) where the trait WithFooBar inherits from WithFoo.

Here is my sample code where I attempt to do this:

trait WithFoo {
    fn foo(&self);
}

trait WithFooBar: WithFoo {
    fn bar(&self);
}

struct MyStruct {
}

impl WithFoo for MyStruct {
    fn foo(&self) {
        println!("foo");
    }
}

impl WithFooBar for MyStruct {
    fn bar(&self) {
        println!("bar");
    }
}

fn foobar_caller(wf: &WithFooBar) {
    wf.foo();
    wf.bar();
}

fn invoke(x: &Fn(&WithFoo), y: &MyStruct) {
    x(y);
}

fn main() {
    let data = MyStruct {};
    invoke(&foobar_caller,&data);
}

This fails with the following compile error:

error: type mismatch: the type `fn(&WithFooBar) {foobar_caller}` implements the trait `for<'r> std::ops::Fn<(&'r WithFooBar + 'r,)>`, but the trait `for<'r> std::ops::Fn<(&'r WithFoo + 'r,)>` is required (expected trait `WithFoo`, found trait `WithFooBar`) [--explain E0281]

I understand that the error is saying that &Fn(&WithFooBar) is not &Fn(&WithFoo), but given that the WithFooBar trait inherits from WithFoo, is seems like it should be possible to pass a pointer to this function.

Is it possible to somehow "downcast" the function pointer to type &Fn(&WithFoo)? I have tried both this:

 let f = &foobar_caller as &Fn(&WithFoo);
 invoke(f,&data);

and this:

 let f: &Fn(&WithFoo) = &foobar_caller;
 invoke(f,&data);

But neither of these attempts works.

(This example is on the rust playground here.)

Andrew Straw
  • 1,138
  • 12
  • 15

1 Answers1

2

No, you can't do this. First off, Rust doesn't support downcasting a trait object to another trait object. With the Any trait, you could downcast a trait object to the underlying object's concrete type and then get a trait object for a different type, but that requires knowing the object's concrete type, which you don't know – that's the whole point of trait objects!

Furthermore, what you're trying to do is not sound: a function expecting to receive a &WithFooBar should not be passed a &WithFoo, because the WithFoo might not implement WithFooBar. If it was the other way around though, it would be sound, but even though WithFooBar inherits from WithFoo, Rust doesn't allow casting a &WithFooBar to a &WithFoo, so even if we tried to make a wrapper function, it wouldn't work.

Francis Gagné
  • 60,274
  • 7
  • 180
  • 155