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

问题描述 投票:0回答:3

如何从这段代码中的

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
rust traits
3个回答
143
投票

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
的要求,它仍然无法正常工作。第一个问题是
A
中的
Box<dyn A>
also 实现
Any
...这意味着当您调用
downcast_ref
时,您实际上是在对象类型
A
上调用它。
Any
只能向下转换为调用它的类型,在本例中为 A
,因此您只能向下转换回您已经拥有的 
&dyn A

但是那里有

Any

的底层类型的实现
某处,对吧?嗯,是的,但是你无法理解它。 Rust 不允许您从 &dyn A
&dyn Any
“交叉转换”。

That 就是 as_any

 的用途;因为它只是在我们的“具体”类型上实现的东西,所以编译器不会对应该调用哪一个类型感到困惑。在 
&dyn A
 上调用它会导致它动态分派到具体实现(同样,在本例中为 
B::as_any
),具体实现使用 
&dyn Any
 的实现返回 
Any
,这就是我们要实现的想要。
请注意,您
可以

通过不使用

B来回避整个问题

。具体来说,以下内容
有效: A

但是,这会阻止您使用任何“其他”方法;
您在这里可以做的所有事情都是向下转换为具体类型。

作为潜在兴趣的最后一点,mopa 板条箱允许您将 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!") }; } 的功能与您自己的特性相结合。

应该清楚的是,如果有另一种类型

Any
实现

C

9
投票
A

转换为

Box<C>
,则转换可能会失败。我不知道你的情况,但对我来说,看起来很像你正在将 Java 等其他语言的技术引入 Rust。我在 Rust 中从未遇到过这种问题——也许你的代码设计可以改进以避免这种类型的强制转换。

如果你愿意,你可以用
Box<B>
“投射”几乎任何东西。遗憾的是,如果我们只想将 

mem::transmute

转换为 Box<A>

Box<B> 转换为
&A
,就会遇到问题,因为指向
&B
的指针是一个胖指针,实际上由两个指针组成:一个指向实际对象,一个到 vptr。如果我们将其转换为
trait
类型,我们可以忽略 vptr。请记住,这个解决方案非常不安全并且非常老套——我不会在“真实”代码中使用它。


struct


编辑:管他的,这比我想象的更不安全。如果你想以这种方式正确地做到这一点,你必须使用

let (b, vptr): (Box<B>, *const ()) = unsafe { std::mem::transmute(a) }; 。但这仍然不稳定。我不认为这对OP有任何用处;不要使用它!

在这个非常相似的问题中有更好的选择:如何匹配特征实现者


    

std::raw::TraitObject 相当不错的编程语言! ngl 当我仍然使用这个语言时,这种东西会让我怀疑我的理智。也许我的大脑不够大,无法理解这样做的吸引力......


0
投票
© www.soinside.com 2019 - 2024. All rights reserved.