如何从此代码中的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>;
}
此代码返回错误:
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
答案 0 :(得分:45)
在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!"),
};
}
另一种方法是为基本特征(在本例中为A
)的每个“目标”实现一个方法,并为每个所需的目标类型实现强制转换。
等等,为什么我们需要as_any
?
即使您将Any
添加为A
的要求,它仍然无法正常运行。第一个问题是A
中的Box<dyn A>
还实施Any
...这意味着当您致电downcast_ref
时,您实际上会在对象类型A
上调用它。 Any
只能 向下广播到它所调用的类型,在这种情况下为A
,因此您只能转发回&dyn A
你已经拥有了。
但是某处的基础类型有Any
的实现,对吗?嗯,是的,但你无法得到它。 Rust不允许您从&dyn A
“转换”到&dyn Any
。
那是as_any
的用途;因为它只是在我们的“具体”类型上实现的,所以编译器不会对它应该调用哪一个感到困惑。在&dyn A
上调用它会导致它动态调度到具体实现(在这种情况下,再次,B::as_any
),它使用&dyn Any
的实现返回Any
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!")
};
}
但是,这使您无法使用任何其他方法;你可以在这里做的所有是一个具体的类型。
作为潜在兴趣的最后一点,mopa包可让您将Any
的功能与您自己的特征结合起来。
答案 1 :(得分:4)
应该很清楚,如果有另一种类型C
实施A
并且您尝试将Box<C>
投射到Box<B>
,则广告可能会失败。我不了解你的情况,但对我来说,它看起来很像你将其他语言(如Java)的技术带入Rust。我从未在Rust中遇到过这种问题 - 也许你的代码设计可以改进以避免这种类型的演员。
如果你愿意,你可以&#34;演员&#34;几乎任何mem::transmute
的东西。遗憾的是,如果我们只想将Box<A>
转换为Box<B>
或&A
转换为&B
,我们就会遇到问题,因为指向trait
的指针是胖的实际上由两个指针组成的指针:一个指向实际对象,一个指向vptr。如果我们将其转换为struct
类型,我们可以忽略vptr。请记住,这个解决方案是非常不安全和非常hacky - 我不会在&#34;真实&#34;码。
let (b, vptr): (Box<B>, *const ()) = unsafe { std::mem::transmute(a) };
std::raw::TraitObject
。但这仍然不稳定。我不认为这对OP有用;不要用它!
在这个非常相似的问题中有更好的选择:How to match trait implementors