I thought I found a nice little trick to find out if an object implements a certain trait.
fn main() {
let user = User::from(10u32);
println!("{}", user.valid);
let bob = Box::new(Bob{});
let user = User::from(bob); // not satisfied
println!("{}", user.valid);
let baz = Box::new(Baz{});
let user = User::from(baz); // not satisfied
println!("{}", user.valid);
}
// main trait
trait Animal {
fn run(&self);
}
// main struct
struct User {
pub valid: bool,
}
impl From<u32> for User { // specific type
fn from(v: u32) -> Self {
Self{ valid: true }
}
}
impl From<Box<dyn Animal>> for User { // specific type
fn from(v: Box<dyn Animal>) -> Self {
Self{ valid: true }
}
}
impl From<Box<dyn std::any::Any>> for User { // all unknown types
fn from(v: Box<dyn std::any::Any>) -> Self {
Self{ valid: false }
}
}
// input struct A
struct Bob {}
impl Animal for Bob {
fn run(&self) {}
}
// input struct B
struct Baz {}
Well, no it won't work. Is it possible to implement the From<dyn> as shown above?
Well, the implementation works. The call doesn’t work the way you did it.
Because the argument type of User::from is generic, Rust won’t introduce any implicit coercions for the argument (that’s just how Rust’s type inference currently works). Sometimes it helps to allow coercions nonetheless by adding a cast with unspecified target, e.g. User::from(bob as _), but for this case, it just produces a different error message.
error[E0282]: type annotations needed
--> src/main.rs:6:27
|
6 | let user = User::from(bob as _); // not satisfied
| ^^^^^^^^ cannot infer type
|
= note: type must be known at this point
ultimately the problem is: Rust has no preference whatsoever between dyn Any and dyn Animal, so it will never decide for one of the two for you. You can do it explicitly with User::from(bob as Box<dyn Animal>), but of course this kills your “nice little trick”.
IMO your “trick” is not something all that useful to begin with, even if it would work as you expected. You’re converting compile-time information (i.e. what trait a type implements) into run-time information, but the way it would be done would be quite suboptimal: What if the User::from(…) call happens to a Box<T> where T is a generic type argument of a function the User::from(…) call lives in? At compile-time it couldn’t become a Box<dyn Animal> when there isn’t a T: Animal bound, but the function could still be called with T == Bob.
There’s a WIP feature to allow such checks in Rust without this shortcoming, called “specialization”, but that feature will probably still take quite a while until it’s available.