【问题标题】:Is it possible to check if an object implements a trait at runtime?是否可以在运行时检查对象是否实现了特征?
【发布时间】:2015-05-16 20:37:13
【问题描述】:
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");}
        }
        */
    }
}

我想知道这样的事情是否可能?

let health = a.get_trait_object::<Health>();
match health{
    Some(h) => {h.health();},
    None => {println!("Has no Health trait");}
}

【问题讨论】:

  • 目前有一组 RFC 用于在 Rust 中进行向下转换(以某种方式)。总有一天会实现的,但目前还不行。

标签: rust


【解决方案1】:

目前在 Rust 中不可能做到这一点,也不太可能实现;但是,可以将类似的抽象构造为您的 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 预计会在某个时候出现负边界;届时,您将能够拥有这样的东西:

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();

这会将样板从每个 trait 的实现减少到每个 trait 一个,但是那个阶段还没有到来。不过,您可以采取两种方法的中间立场:

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

尝试不同的处理方式,直到找到自己喜欢的东西!可能性是无限的! (因为 Rust 是图灵完备的。)

【讨论】:

    【解决方案2】:

    从 1.0 开始,没有。 Rust 不提供任何动态向下转换支持,Any 除外;但是,这只允许您向下转换为值的特定具体类型,而不是该具体类型实现的任意特征。

    相信你可以手动实现这种转换,但这需要不安全的代码,很容易出错;不是我想在 SO 答案中尝试和总结的那种事情。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-02-21
      • 1970-01-01
      • 2015-11-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-13
      相关资源
      最近更新 更多