I have a collection of Trait
, a function that iterates over it and does something, and then I would like to check the implementor type and if it is of type Foo
then downcast it and call some Foo method.
Basically, something similar to Go's type-switch and interface conversion.
Searching around I found about the Any trait but it can only be implemented on 'static
types.
To help demonstrate what I want:
let vec: Vec<Box<Trait>> = //
for e in vec.iter() {
e.trait_method();
// if typeof e == Foo {
// let f = e as Foo;
// f.foo_method();
//}
}
As you have noticed, downcasting only works with
Any
trait, and yes, it only supports'static
data. You can find a recent discussion on why it is so here. Basically, implementing reflection for references of arbitrary lifetimes is difficult.It is also impossible (as of now, at least) to combine
Any
with your custom trait easily. However, a macro library for automatic implementation ofAny
for your trait has recently been created. You can also find some discussion on it here.This isn't a Rust-specific problem, although the vocabulary may be a little different. The ideal way to solve a problem like this, not just with traits in Rust but in any language, is to add the desired behavior (
foo_method
in your example) to the abstract interface (Trait
):In this example, I have put a default implementation of
foo_method
inTrait
which does nothing, so that you don't have to define it in everyimpl
but only the one(s) where it applies. You should really attempt to make the above work before you resort to downcasting to a concrete type, which has serious drawbacks that all but erase the advantages of having trait objects in the first place.That said, there are cases where downcasting may be necessary, and Rust does support it -- although the interface is a little clunky. You can downcast
&Trait
to&Foo
by adding an intermediate upcast to&Any
:as_any
has to be a method inTrait
because it needs access to the concrete type. Now you can attempt to callFoo
methods on aTrait
trait object like this (complete playground example):To make this work, you have to specify what type you expect (
::<Foo>
) and useif let
to handle what happens when the referenced object is not an instance ofFoo
. You can't downcast a trait object to a concrete type unless you know exactly what concrete type it is.But if you ever need to know the concrete type, trait objects are almost useless anyway! You probably should use an
enum
instead, so that you will get compile-time errors if you omit to handle a variant somewhere. Furthermore, you can't useAny
with non-'static
structs, so if anyFoo
might need to contain a reference, this design is a dead end. The best solution, if you can do it, is to addfoo_method
to the trait itself.