我可以对 trait 对象进行类型自省,然后向下转型吗?

Can I do type introspection with trait objects and then downcast it?

我有一个 Trait 的集合,一个迭代它并做一些事情的函数,然后我想检查实现类型,如果它是 Foo 类型,那么向下转换它并调用一些 Foo 方法。

基本上,类似于 Go 的 type-switch and interface conversion

四处搜索我发现了 Any trait 但它只能在 'static 类型上实现。

为了帮助展示我想要的东西:

let vec: Vec<Box<Trait>> = //

for e in vec.iter() {
    e.trait_method();

    // if typeof e == Foo {
    // let f = e as Foo;
    // f.foo_method();
    //}
}

如您所见,向下转型仅适用于 Any 特征,是的,它仅支持 'static 数据。您可以找到最近关于为什么如此的讨论 here。基本上,为任意生命周期的引用实现反射是困难的。

也不可能(至少到目前为止)将 Any 与您的自定义特征轻松结合。但是,macro library for automatic implementation of Any for your trait has recently been created. You can also find some discussion on it here.

这不是特定于 Rust 的问题,尽管词汇可能略有不同。解决这样的问题的理想方法,不仅是 Rust 中的特征,而且是任何语言中的特征,都是将所需的行为(在您的示例中为 foo_method)添加到抽象接口(Trait):

trait Trait {
    fn trait_method(&self);
    fn foo_method(&self) {} // does nothing by default
}

struct Foo;

impl Trait for Foo {
    fn trait_method(&self) {
        println!("In trait_method of Foo");
    }

    fn foo_method(&self) {
        // override default behavior
        println!("In foo_method");
    }
}

struct Bar;

impl Trait for Bar {
    fn trait_method(&self) {
        println!("In trait_method of Bar");
    }
}

fn main() {
    let vec: Vec<Box<dyn Trait>> = vec![Box::new(Foo), Box::new(Bar)];

    for e in &vec {
        e.trait_method();
        e.foo_method();
    }
}

在此示例中,我在 Trait 中放置了 foo_method 的默认实现,它什么都不做,因此您不必在每个 impl 中定义它,而只需适用的地方。你应该真的尝试在你求助于向下转型到具体类型之前完成上述工作,这有严重的缺点,几乎消除了首先拥有特征对象的优势。

也就是说,在某些情况下可能需要向下转型,Rust 确实支持它——尽管界面有点笨拙。您可以通过添加中间 upcast 到 &Any:

来向下转换 &Trait&Foo
use std::any::Any;

trait Trait {
    fn as_any(&self) -> &dyn Any;
}

struct Foo;

impl Trait for Foo {
    fn as_any(&self) -> &dyn Any {
        self
    }
}

fn downcast<T: Trait + 'static>(this: &dyn Trait) -> Option<&T> {
    this.as_any().downcast_ref()
}

as_any 必须是 Trait 中的方法,因为它需要访问具体类型。现在您可以尝试在 Trait 特征对象上调用 Foo 方法,如下所示 (complete playground example):

if let Some(r) = downcast::<Foo>(&**e) {
    r.foo_method();
}

要使其工作,您必须指定您期望的类型 (::<Foo>) 并使用 if let 来处理当引用的对象不是 Foo 的实例时发生的情况.除非你确切地知道什么具体类型,否则你不能将特征对象向下转换为具体类型。

如果你需要知道具体的类型,那么 trait 对象几乎毫无用处!您可能应该改用 enum ,这样如果您在某处忽略处理变体,就会出现编译时错误。此外,您不能将 Any 与非 'static 结构一起使用,因此如果任何 Foo 可能需要包含引用,则此设计是死胡同。如果可以的话,最好的解决方案是将 foo_method 添加到特征本身。