在Rust中进行向下转换有两种方法。第一种方法是使用Any
。请注意,这仅允许您向下转换为确切的、原始的具体类型。只需按以下方式操作:
use std::any::Any;
trait A {
fn as_any(&self) -> &dyn Any;
}
struct B;
impl A for B {
fn as_any(&self) -> &dyn Any {
self
}
}
fn main() {
let a: Box<dyn A> = Box::new(B);
let b: &B = match a.as_any().downcast_ref::<B>() {
Some(b) => b,
None => panic!("&a isn't a B!"),
};
}
另一种方法是在基本特征(在本例中为
A
)上为每个“目标”实现一个方法,并为每个所需的目标类型实现转换。
等等,我们为什么需要as_any
?
即使将
Any
添加为要求
A
的条件,它仍然无法正确工作。首先问题是在
Box<dyn A>
中的
A
也将实现
Any
...这意味着当您调用
downcast_ref
时,您实际上将在对象类型
A
上调用它。
Any
只能向其调用的类型进行向下转换,在这种情况下是
A
,因此您只能将其转换回
&dyn A
,而您已经拥有它。
但是,其中包含底层类型的
Any
实现吗?好吧,是的,但您无法获取它。 Rust不允许您从
&dyn A
转换为
&dyn Any
。
as_any
就是为此而设计的;因为它仅在我们的“具体”类型上实现,所以编译器不会混淆应该调用哪一个。在
&dyn A
上调用它会导致它动态分派到具体的实现(在这种情况下是
B::as_any
),后者使用
B
的
Any
实现返回
&dyn Any
,这正是我们想要的。
请注意,您可以通过根本不使用
A
来回避整个问题。 具体来说,以下内容也将有效:
fn main() {
let a: Box<dyn Any> = Box::new(B);
let _: &B = match a.downcast_ref::<B>() {
Some(b) => b,
None => panic!("&a isn't a B!")
};
}
然而,这将使你无法使用其他方法;在这里你只能将其向下转换为一个具体类型。
最后值得一提的是,
mopa crate 可以让你将
Any
的功能与自己的 trait 结合起来。
as_any
函数。这个函数被实现在类型B
中,它接受一个类型为&B
的参数self
,该参数会被转换为一个&Any
类型,然后可以再次转换回一个&B
类型。如果将a.as_any()
替换为(&*a as &Any)
,那么它只能被转换回转换成&Any
类型的那个类型,也就是&A
。&A
和&B
不是同一种类型,因为它们有不同的虚表。 - dhardy