这不是 Rust 特有的问题,尽管词汇表可能有点不同。解决此类问题的理想方法,不仅仅是使用 Rust 中的特征,而是使用任何语言,是将所需的行为(在您的示例中为 foo_method)添加到抽象接口(@987654323@):
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 中定义它,而只需在它适用的那个(S)中定义它.您应该真的尝试在向下转换为具体类型之前完成上述工作,这具有严重的缺点,几乎完全消除了首先具有特征对象的优点。
也就是说,在某些情况下可能需要向下转换,Rust 确实支持它——尽管接口有点笨拙。您可以通过向&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 中的一个方法,因为它需要访问具体类型。现在你可以尝试像这样 (complete playground example) 对 Trait 特征对象调用 Foo 方法:
if let Some(r) = downcast::<Foo>(&**e) {
r.foo_method();
}
要完成这项工作,您必须指定您期望的类型 (::<Foo>) 并使用 if let 来处理当引用的对象不是 Foo 的实例时发生的情况。除非您确切知道 what 它是具体类型,否则您不能将 trait 对象向下转换为具体类型。
如果您需要知道具体类型,那么 trait 对象几乎是无用的!您可能应该改用enum,这样如果您忽略在某处处理变体,您将得到编译时错误。此外,您不能将Any 与非'static 结构一起使用,因此如果任何Foo 可能需要包含引用,则此设计是死路一条。如果可以的话,最好的解决方案是将foo_method 添加到特征本身。