How can I write a function that accepts several types of trait objects?

Hi there.
As title, how can I write a function that accepts both Box<dyn Foo> and &Box<dyn Foo>?
More specifically, in my case, I would like to get a function that accepts Vec<Box<dyn Foo>> and Vec<&Box<dyn Foo>>.
I thought both implemented Foo, so function like the following should work fine:

fn accept_foos<T: Foo>(foos: Vec<T>) {
    // some operations...
}

but actually this wouldn't work at all. The error message I got:

error[E0277]: the trait bound `Box<dyn Foo>: Foo` is not satisfied
  --> src/main.rs:20:17
   |
5  | fn accept_foos<T: Foo>(foos: Vec<T>) {
   |                   --- required by this bound in `accept_foos`
...
20 |     accept_foos(foos1);
   |                 ^^^^^ the trait `Foo` is not implemented for `Box<dyn Foo>`

error[E0277]: the trait bound `&Box<dyn Foo>: Foo` is not satisfied
  --> src/main.rs:21:17
   |
5  | fn accept_foos<T: Foo>(foos: Vec<T>) {
   |                   --- required by this bound in `accept_foos`
...
21 |     accept_foos(foos2);
   |                 ^^^^^ the trait `Foo` is not implemented for `&Box<dyn Foo>`

You can try this in the playground

The compiler says Box<dyn Foo> doesn't implement Foo. Neither do &Box<dyn Foo>. This fact really confused me.

Any ideas would be really appreciated! Thanks.

If you want them to implement Foo, you have to provide an implementation.

impl<T: ?Sized + Foo> Foo for Box<T> {}
impl<T: ?Sized + Foo> Foo for &'_ T {}

Thanks for the quick reply!
So if Foo is defined in an external crate I don't have control over, is there no way to write a function I want?
I gave a generic function as an example because this was what I tried, but I'm not particular about how it is achieved. That is, I can do something on the caller side rather than the callee side (i.e. function definition).

That's right, these impls needs to be provided by the creator of the trait

Box<T> has a AsRef<T> implementation so

fn accept_foos<T: AsRef<dyn Foo>>(foos: Vec<T>) {
    // some operations
    for foo in foos {
        let _a_foo: &dyn Foo = foo.as_ref(); // getting a `dyn Foo` from `T``
    }
}

should work

2 Likes

You can define your own trait that does the same thing:

trait AsFoo {
    fn as_foo(&self)->&dyn Foo;
}

impl AsFoo for dyn Foo {
    fn as_foo(&self)->&dyn Foo { self }
}

impl<T:AsFoo+?Sized> AsFoo for &'_ T {
    fn as_foo(&self)->&dyn Foo {
        T::as_foo(*self)
    }
}

impl<T:AsFoo+?Sized> AsFoo for Box<T> {
    fn as_foo(&self)->&dyn Foo {
        T::as_foo(&**self)
    }
}
5 Likes

Oh this is perfect, thanks so much! Works for me.

Thanks, this also looks very useful and should work!

The advantage of @2e71828 solution is that it also works for &dyn Foo for my version you would need to
implement AsRef<dyn Foo> for &dyn Foo which you can't do if Foo is from a external crate.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.