How to test whether generic type implements trait

Is it possible? Here's pseudocode for what I'm trying to do:

trait Foo {
    fn foo(&self) {}
}

fn scan<T>(object: &T) {
    if let f = object as &dyn Foo {
        f.foo();
    }
}

What you're trying to do is not possible in Rust. Or perhaps not possible yet in case Rust ever gets some form of specialization.

However in case you're asking this not only to learn more about features and non-features of the programming language Rust, but because you believe it would help solve some practical problem you're working on, feel free to avoid any XY issues by telling us more about the problem at hand. Checking for a trait implementation at run-time feels similar to downcasting to a subclass in OOP languages so that people might try looking for some way to do some kind of downcasting in cases where class downcasting was appropriate in their OOP code, even when Rust might offer far better alternatives for many use-case, in particular enum types.

3 Likes

Thanks, I was afraid it wasn't possible. I'm just exploring the design space right now. It's a bit complicated for me to describe it all and ask the internet to do my work for me lol

I would kill to be able to use specialization tho

W.r.t. specialization, your example code would hit the hard soundness problem which is currently blocking most progress on a specialization feature: If some type Bar<'a, 'b> only implements Foo if e.g. the lifetimes match (impl<'a> Foo for Bar<'a, 'a>) then that implementation gets to rely on those lifetimes being the same; however, lifetimes are a compile-time-only concept that only exists in rather early stages of compilation (and the “exact value” that lifetime parameters take is also very often under-specified) which makes it hard, or even impossible for the compiler to figure in the general case whether or not some monomorphization of scan<T> does or doesn’t fulfill the T: Foo bound.

The current implementation of specialization is unsound exactly for reasons relating to this kind of problem; the current implementation would incorrectly consider Bar<'a, 'b> to implement Foo for the purposes of specialization, even with a restricted implementation for Bar<'a, 'a> only.

If you don’t care about lifetimes (which isn’t unlikely), then for T: 'static types, such soundness problems don’t exist, but of course specialization isn’t stable nonetheless.

Sometimes for T: 'static types, using the Any trait can be useful for some form of “downcasting”; however this only works for concrete types.

To support code similar to your pseudo-code, one possibly useful approach might be to have a trait CheckIfFoo { fn downcast_to_foo(&self) -> Option<&dyn Foo> } and implement it for all types that you want to support with your scan function (its signature would become scan<T: CheckIfFoo>(…). Reducing boilerplate for such CheckIfFoo implementations is possible using macros. So if you have, say, a long but finite list of types A, B, C that don’t implement Foo, and types D, E, F that do implement Foo, those implementations might be as short as one macro call check_if_foo_none!(A, B, C); and another macro-call check_if_foo_some!(D, E, F);.

2 Likes

Thanks for these ideas! I do only care about 'static. I found a crate called cast_trait_object that implements something like CheckIfFoo so I'm going to use that.

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.