It's possible, but you're going to run into more problems, I'm afraid.
The main problem is that is
is a method impl on dyn Any
, not part of the trait. If you look at it's documentation, you can see that it's in impl dyn Any
: Any in std::any - Rust
The reason you get lifetime errors, then, is because it isn't treating dyn Foo
as dyn Any
. Rather it's trying to cast one of the other layers into a dyn Any
: it's trying to go from &&&dyn Foo
into &dyn Any
, and the fact that the first and second layers of references (introduced by filter, and iter respectively) are temporary.
If you change the code to unpack those (and call is
directly on &dyn Any
), you'll see a different error:
fn test2(list: &[&dyn Foo]) -> bool{
list.iter().filter(|&&b| Any::is::<Bar>(b)).count() == 0
}
error[E0308]: mismatched types
--> src/main.rs:14:45
|
14 | list.iter().filter(|&&b| Any::is::<Bar>(b)).count() == 0
| ^ expected trait `std::any::Any`, found trait `Foo`
|
= note: expected reference `&(dyn std::any::Any + 'static)`
found reference `&dyn Foo`
(playground)
Now, as for fixing it, you have a few options - but there isn't a single really "good" one. Any::is
is, well, a method only available if you have a dyn Any
- not dyn SomeOtherTrait
. To make matters worse, rust has no way to cast to supertraits - unlike languages like Java, there's no inherent way to go from dyn Any
to dyn SomeOtherTrait
- the vtable ptr needed to create dyn Any
literally isn't stored in dyn Foo
.
To fix this, you need to have a method which creates a dyn Any
from your dyn Foo
, and is somehow recompiled for each instance of dyn Foo
. The easiest way I've found to do this is to use an auxiliary trait which is auto-implemented for everything concrete, and then to require it as a dependency to bring it into the trait object:
trait Foo: Any + FooExt {}
trait FooExt: Any { // requires same traits as Foo
fn as_any(&self) -> &dyn Any;
}
impl<T> FooExt for T where T: Foo + Sized { // but it's only implemented if also Sized
fn as_any(&self) -> &dyn Any {
self
}
}
(playground with this impl)
This is a bit tricky, but it should put a limited burden on implementors of the trait, while still offering functionality. I would recommend sticking it in a default method in Foo
itself, but unfortunately you can't do this cast without the Self: Sized
bound, and I don't think there's a way to stick the implementation inside Foo
itself without rust then complaining when you make dyn Foo
trait objects. By using the separate trait, Foo
itself doesn't have to care at all that the implementation requires Sized
. It only cares that something provides the as_any
function, so it can still exist as dyn Foo
. It'll end up requiring extra effort to implement Foo
for anything which isn't Sized
, but I think that's reasonable as those types won't necessarily have any way to conver to dyn Any
in any case.
Hope that helps! I think something like this could really end up useful as a utility library, and one probably exists, but I don't know where it is. I've written a number of weird hacks like this, but they all end up being weird and specific - so making utility crates rarely seems worth it.
Edit: looks like my answer overlaps with @mbrubeck's a bit! Leaving it as is since I think both are probably helpful.