在 Rust 中有两种方法可以进行向下转换。第一种是使用Any。请注意,此 only 允许您向下转换为确切的原始具体类型。像这样:
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);
// The indirection through `as_any` is because using `downcast_ref`
// on `Box<A>` *directly* only lets us downcast back to `&A` again.
// The method ensures we get an `Any` vtable that lets us downcast
// back to the original, concrete type.
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 时,您实际上会在对象类型@ 上调用它987654334@。 Any 只能向下转换为调用它的类型,在本例中为A,因此您只能向下转换为您已经拥有的&dyn A。
但是某处中的底层类型有一个Any 的实现,对吧?嗯,是的,但你无法做到这一点。 Rust 不允许你从&dyn A“交叉转换”到&dyn Any。
那是as_any的用途;因为它只在我们的“具体”类型上实现,所以编译器不会混淆它应该调用哪一个。在&dyn A 上调用它会使其动态分派到具体实现(同样,在本例中为B::as_any),它使用Any 的实现为B 返回一个&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 的功能与您自己的特征结合起来。