简体   繁体   English

如何从特征对象获取对具体类型的引用?

[英]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 .第一个问题是, ABox<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.

相关问题 如何将特征转换为具体类型? - How to convert trait to concrete type? 如何从盒装特征 object 中获取对结构的引用? - How to get reference to struct from boxed trait object? 从向量获取特征对象引用 - Get a trait object reference from a vector 如何反序列化为特征,而不是具体类型? - How do I deserialize into trait, not a concrete type? 如何返回对添加到特征对象向量的具体类型的引用? - How do I return a reference to a concrete type that was added to a vector of trait objects? 如何将特征对象的存根插入Rust中的类型并对其进行引用? - How to inject a stub for a trait object into a type in Rust and keep a reference to it? 如何将具体类型的迭代器特征对象转换为特征对象的迭代器特征对象? - How can I transform an iterator trait object of concrete types into an iterator trait object of trait objects? 如何将混凝土盒装 object 转换为盒装特征 object - How to convert a concrete boxed object to a boxed trait object 如何从具有关联类型的包装特征 object 获取特征 object? - How do I obtain a trait object from a wrapper trait object with an associated type? 如何在特征本身上写一个特征绑定来引用关联类型? - How to write a trait bound for a reference to an associated type on the trait itself?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM