[英]How to get a reference to a concrete type from a trait object?
How do I get Box<B>
or &B
or &Box<B>
from the a
variable in this code:如何从此代码中的
a
变量获取Box<B>
或&B
或&Box<B>
:
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>;
}
This code returns an error:此代码返回错误:
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
There are two ways to do downcasting in Rust.在 Rust 中有两种方法可以进行向下转换。 The first is to use
Any
.第一种是使用
Any
。 Note that this only allows you to downcast to the exact, original concrete type.请注意,这仅允许您向下转换为准确的原始具体类型。 Like so:
像这样:
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!"),
};
}
The other way is to implement a method for each "target" on the base trait (in this case, A
), and implement the casts for each desired target type.另一种方法是为基本特征(在本例中为
A
)上的每个“目标”实现一个方法,并为每个所需的目标类型实现强制转换。
Wait, why do we need as_any
?等等,为什么我们需要
as_any
?
Even if you add Any
as a requirement for A
, it's still not going to work correctly.即使您添加
Any
作为A
的要求,它仍然无法正常工作。 The first problem is that the A
in Box<dyn A>
will also implement Any
... meaning that when you call downcast_ref
, you'll actually be calling it on the object type A
.第一个问题是,
A
在Box<dyn A>
也将实施Any
...这意味着,当你打电话downcast_ref
,您实际上在对象类型调用它A
。 Any
can only downcast to the type it was invoked on, which in this case is A
, so you'll only be able to cast back down to &dyn A
which you already had. Any
只能向下转换为调用它的类型,在本例中为A
,因此您只能向下转换为您已经拥有的&dyn A
。
But there's an implementation of Any
for the underlying type in there somewhere , right?但是在某处有一个底层类型的
Any
实现,对吧? Well, yes, but you can't get at it.嗯,是的,但你不能理解它。 Rust doesn't allow you to "cross cast" from
&dyn A
to &dyn Any
. Rust 不允许你从
&dyn A
到&dyn Any
“交叉转换”。
That is what as_any
is for;这就是
as_any
的用途; because it's something only implemented on our "concrete" types, the compiler doesn't get confused as to which one it's supposed to invoke.因为它只在我们的“具体”类型上实现,所以编译器不会混淆它应该调用哪个类型。 Calling it on an
&dyn A
causes it to dynamically dispatch to the concrete implementation (again, in this case, B::as_any
), which returns an &dyn Any
using the implementation of Any
for B
, which is what we want.在
&dyn A
上调用它会导致它动态分派到具体实现(同样,在本例中为B::as_any
),它使用Any
for B
的实现返回一个&dyn Any
,这正是我们想要的。
Note that you can side-step this whole problem by just not using A
at all .说明只是没有使用,你可以避开这个整个问题
A
都没有。 Specifically, the following will also work:具体来说,以下也将起作用:
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!")
};
}
However, this precludes you from having any other methods;但是,这使您无法使用任何其他方法; all you can do here is downcast to a concrete type.
你可以在这里做是垂头丧气的具体类型。
As a final note of potential interest, the mopa crate allows you to combine the functionality of Any
with a trait of your own.作为潜在兴趣的最后一点, mopa crate 允许您将
Any
的功能与您自己的特征相结合。
It should be clear that the cast can fail if there is another type C
implementing A
and you try to cast Box<C>
into a Box<B>
.很明显,如果有另一个类型
C
实现A
并且您尝试将Box<C>
转换为Box<B>
,则转换可能会失败。 I don't know your situation, but to me it looks a lot like you are bringing techniques from other languages, like Java, into Rust.我不知道您的情况,但在我看来,您似乎正在将其他语言(如 Java)的技术引入 Rust。 I've never encountered this kind of Problem in Rust -- maybe your code design could be improved to avoid this kind of cast.
我从未在 Rust 中遇到过这种问题——也许可以改进您的代码设计以避免这种类型的转换。
If you want, you can "cast" pretty much anything with mem::transmute
.如果你愿意,你可以用
mem::transmute
来“投射”几乎任何东西。 Sadly, we will have a problem if we just want to cast Box<A>
to Box<B>
or &A
to &B
because a pointer to a trait
is a fat-pointer that actually consists of two pointers: One to the actual object, one to the vptr.可悲的是,如果我们只是想将
Box<A>
为Box<B>
或&A
为&B
就会有问题,因为指向trait
的指针是一个胖指针,实际上由两个指针组成:一个指向实际对象,一到vptr。 If we're casting it to a struct
type, we can just ignore the vptr.如果我们将它转换为
struct
类型,我们可以忽略 vptr。 Please remember that this solution is highly unsafe and pretty hacky -- I wouldn't use it in "real" code.请记住,这个解决方案非常不安全,而且非常笨拙——我不会在“真实”代码中使用它。
let (b, vptr): (Box<B>, *const ()) = unsafe { std::mem::transmute(a) };
EDIT: Screw that, it's even more unsafe than I thought.编辑:搞砸了,它比我想象的更不安全。 If you want to do it correctly this way you'd have to use
std::raw::TraitObject
.如果您想以这种方式正确执行此操作,则必须使用
std::raw::TraitObject
。 This is still unstable though.尽管如此,这仍然不稳定。 I don't think that this is of any use to OP;
我认为这对 OP 没有任何用处; don't use it!
不要使用它!
There are better alternatives in this very similar question: How to match trait implementors在这个非常相似的问题中有更好的选择: 如何匹配特征实现者
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.