如何获得Box<B>或&B或&Box<B>从a在此代码变量:
trait A {}
struct B;
impl A for B {}
fn main() {
let mut a: Box<dyn A> = Box::new(B);
let b = a as Box<B>;
}
Run Code Online (Sandbox Code Playgroud)
此代码返回错误:
error[E0605]: non-primitive cast: `std::boxed::Box<dyn A>` as `std::boxed::Box<B>`
--> src/main.rs:8:13
|
8 | let b = a as Box<B>;
| ^^^^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
Run Code Online (Sandbox Code Playgroud)
DK.*_*DK. 54
有两种方法可以在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);
// 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!"),
};
}
Run Code Online (Sandbox Code Playgroud)
另一种方法是为基本特征(在这种情况下A)上的每个"目标"实现一个方法,并为每个所需的目标类型实现强制转换.
等等,我们为什么需要as_any?
即使你添加Any了一个要求A,它仍然无法正常工作.第一个问题是,A在Box<dyn A>将也实现Any...这意味着,当你打电话downcast_ref,你实际上是在对象类型调用它A. Any可以只低垂到它被调用的类型,在这种情况下A,所以你只能就能够施展回落到&dyn A你已经有了.
但是有一个实施Any的基本类型在那里的某个地方,对不对?嗯,是的,但你无法得到它.锈不你"交叉转换",从允许&dyn A到&dyn Any.
这as_any是为了什么; 因为它只是在我们的"具体"类型上实现的,所以编译器不会对它应该调用哪一个感到困惑.在on上调用它&dyn A会使它动态地调度到具体的实现(在这种情况下,再次B::as_any),它返回一个&dyn Any使用Anyfor 的实现B,这就是我们想要的.
请注意,您可以边一步只是没有使用这整个问题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!")
};
}
Run Code Online (Sandbox Code Playgroud)
但是,这使您无法使用任何其他方法; 你在这里所做的一切都是低调的具体类型.
作为潜在兴趣的最后一点,mopa crate允许您将功能Any与您自己的特征结合起来.
应当明确的是,如果C实现了另一种类型A并且您尝试将其Box<C>转换为,转换可能会失败Box<B>。我不知道您的情况,但是对我来说,这很像是您将其他语言(例如Java)的技术引入了Rust。我在Rust中从未遇到过此类问题-也许可以改进您的代码设计以避免这种转换。
如果需要,您可以使用来“投射”几乎所有内容mem::transmute。可悲的是,如果我们只想强制转换Box<A>为Box<B>或,则会遇到问题&A,&B因为指向traita的指针实际上是一个胖指针,实际上由两个指针组成:一个指向实际对象,一个指向vptr。如果将其强制转换为struct类型,则可以忽略vptr。请记住,此解决方案非常不安全且非常hacky-我不会在“真实”代码中使用它。
let (b, vptr): (Box<B>, *const ()) = unsafe { std::mem::transmute(a) };
Run Code Online (Sandbox Code Playgroud)
编辑:拧,那比我想象的还要不安全。如果您想以这种方式正确执行操作,则必须使用std::raw::TraitObject。但是,这仍然不稳定。我认为这对OP没有任何用处;不要使用它!
在这个非常相似的问题中还有更好的选择:如何匹配特征实现者