Is it possible to check if an object implements a trait at runtime?

后端 未结 2 1135
一个人的身影
一个人的身影 2020-12-10 12:45
trait Actor{
    fn actor(&self);
}
trait Health{
    fn health(&self);
}
struct Plant;
impl Actor for Plant{
    fn actor(&self){
        println!(\"Pla         


        
相关标签:
2条回答
  • 2020-12-10 13:00

    It is not possible to do this in Rust at present, nor is it likely to ever become possible; it is, however, possible to construct similar abstractions as part of your trait:

    trait Actor {
        fn health(&self) -> Option<&Health>;
    }
    
    trait Health { }
    
    impl Actor for Monster {
        fn health(&self) -> Option<&Health> { Some(self) }
    }
    
    impl Health for Monster { }
    
    impl Actor for Plant {
        fn health(&self) -> Option<&Health> { None }
    }
    

    Rust is expected to get negative bounds at some point; when that comes, you’ll be able to have something like this:

    trait MaybeImplements<Trait: ?Sized> {
        fn as_trait_ref(&self) -> Option<&Trait>;
    }
    
    macro_rules! impl_maybe_implements {
        ($trait_:ident) => {
            impl<T: $trait_> MaybeImplements<$trait_> for T {
                fn as_trait_ref(&self) -> Option<&$trait_> {
                    Some(self)
                }
            }
    
            impl<T: !$trait_> MaybeImplements<$trait_> for T {
                fn as_trait_ref(&self) -> Option<&$trait_> {
                    None
                }
            }
        }
    }
    
    impl_maybe_implements!(Health);
    
    trait Actor: MaybeImplements<Health> {
    }
    
    let health: Option<&Health> = actor.as_trait_ref();
    

    This will reduce the boilerplate from every implementation of a trait to just one per trait, but that stage is not yet upon us. Still, you could take the middle ground of the two approaches:

    trait MaybeImplements<Trait: ?Sized> {
        fn as_trait_ref(&self) -> Option<&Trait>;
    }
    
    macro_rules! register_impl {
        ($trait_:ident for $ty:ty) => {
            impl MaybeImplements<$trait_> for $ty {
                fn as_trait_ref(&self) -> Option<$trait_> {
                    Some(self)
                }
            }
        }
    
        (!$trait_:ident for $ty:ty) => {
            impl MaybeImplements<$trait_> for $ty {
                fn as_trait_ref(&self) -> Option<$trait_> {
                    None
                }
            }
        }
    }
    
    register_impl!(Health for Monster);
    register_impl!(!Health for Plant);
    

    Play around with different ways of handling it until you find something you like! The possibilities are limitless! (Because Rust is Turing‐complete.)

    0 讨论(0)
  • 2020-12-10 13:05

    As of 1.0, no. Rust doesn't provide any dynamic downcasting support, with the exception of Any; however, that only allows you to downcast to a value's specific concrete type, not to arbitrary traits that said concrete type implements.

    I believe you could implement such casting manually, but that would require unsafe code that would be easy to get wrong; not the sort of thing I want to try and summarise in an SO answer.

    0 讨论(0)
提交回复
热议问题