Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.4k views
in Technique[技术] by (71.8m points)

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

trait Actor{
    fn actor(&self);
}
trait Health{
    fn health(&self);
}
struct Plant;
impl Actor for Plant{
    fn actor(&self){
        println!("Plant Actor");
    }
}
struct Monster{
    health: f32
}
impl Actor for Monster{
    fn actor(&self){
        println!("Monster Actor");
    }
}
impl Health for Monster{
    fn health(&self){
        println!("Health: {}",self.health);
    }
}
fn main() {
    let plant = Box::new(Plant);
    let monster = Box::new(Monster{health: 100f32});

    let mut actors : Vec<Box<Actor>> = Vec::new();
    actors.push(plant);
    actors.push(monster);

    for a in &actors{
        a.actor();
        /* Would this be possible?
        let health = a.get_trait_object::<Health>();
        match health{
            Some(h) => {h.health();},
            None => {println!("Has no Health trait");}
        }
        */
    }
}

I am wondering if something like this could be possible?

let health = a.get_trait_object::<Health>();
match health{
    Some(h) => {h.health();},
    None => {println!("Has no Health trait");}
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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.)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share

2.1m questions

2.1m answers

63 comments

56.6k users

...