在不同目标之间生锈动态铸造特征对象

时间:2019-04-29 00:03:28

标签: dynamic casting rust

我想知道是否可以将一个特征对象转换为另一个特征对象。

我尝试了以下代码:

trait TraitA {
    fn say_hello(&self) {
        self.say_hello_from_a();
    }
    fn say_hello_from_a(&self);
}

trait TraitB {
    fn say_hello(&self) {
        self.say_hello_from_b();
    }
    fn say_hello_from_b(&self);
}

struct MyType {}

impl TraitA for MyType {
    fn say_hello_from_a(&self) {
        println!("Hello from A");
    }
}

impl TraitB for MyType {
    fn say_hello_from_b(&self) {
        println!("Hello from B");
    }
}

fn main() {
    let a: Box<dyn TraitA> = Box::new(MyType{});    
    let b: Box<dyn TraitB>;

    a.say_hello();
    b = a;
    b.say_hello();

}

我收到以下编译错误:

error[E0308]: mismatched types
  --> trait_objects.rs:34:9
   |
34 |     b = a;
   |         ^ expected trait `TraitB`, found trait `TraitA`
   |
   = note: expected type `std::boxed::Box<dyn TraitB>`
              found type `std::boxed::Box<dyn TraitA>`

所以我试图做的是声明2个特征和一个名为 MyType 的类型。然后,我实现了MyType的两个特征。我创建了一个MyType类型的新特征对象TraitA,我将其称为 a 。但是由于 a 也实现了TraitB,所以我认为应该可以将其转换为TraitB。

还没有想出是否有可能。如果是这样,如何将特征对象a转换为TraitB。

注意:在C ++中,出于相同的目的,我通常会使用类似于std::dynamic_pointer_cast<TraitB>(a);的东西。

谢谢

编辑: 我添加了一个示例,其中可以使用横向浇铸。

比方说,我有一个结构,其中包含一些表示某些现实生活实体的数据:

struct MyType {
    let a: i32;
    let b: i32;
}

这种类型的实例可以在代码库的至少两个不同部分中使用。在这两个部分上,我都需要一个名为get_final_value的行为。

有趣的是,get_final_value的响应取决于调用它的人。

  • 我为什么不将类型分为2个不同的类型?:从技术上讲,ab属于同一类,并不是说get_final_value()既使用了两者又使用了值来计算结果。

  • 为什么不使用泛型/静态调度?因为MyType只是一个示例。在实际情况下,我有不同的structs,他们都以不同的方式实现了这两个特征。

  • 为什么不使用Any特征?老实说,直到最近我才知道它的存在。我不记得 Rust编程语言书中提到它的情况。无论如何,似乎您需要了解具体类型,才能将Any转换为该具体类型,然后转换为trait对象。

2 个答案:

答案 0 :(得分:2)

另一种选择可能是创建同时使用TraitA和TraitB作为特征的特征,并为每种类型提供强制转换:

<!DOCTYPE html>
<html>

<head>
    <link rel="stylesheet" type="text/css" href="./index.css" />

</head>

<body>
    <audio autoplay controls>
      <source src="./biker.mp3" type="audio/mpeg">
   </audio>
   <main>
      <div class="bike">


           <img  src="./public/images/biker1.png">
      </div>
   </main>


</body>

</html>

然后让MyType实现它:

trait TraitC : TraitA + TraitB {
    fn as_trait_a(&self) -> &dyn TraitA;
    fn as_trait_b(&self) -> &dyn TraitB;
}

一旦这样做,您就可以对Box使用TraitC,并同时使用TraitA和TraitB的程序逻辑。

主要示例以显示各种使用方式:

impl TraitC for MyType {
    fn as_trait_a(&self) -> &dyn TraitA {self}
    fn as_trait_b(&self) -> &dyn TraitB {self}
}

如果A和B确实确实属于在一起,那么这更好地表示了这一点,并且仍然让您可以自由地根据需要单独使用它们。

链接到Rust Playground

答案 1 :(得分:1)

使用Box<MyType>代替Box<dyn Trait>解决了这个问题。

fn main() {
    let a = Box::new(MyType {});

    TraitA::say_hello(&*a);
    TraitB::say_hello(&*a);
}

在这种情况下,无需使用特征对象。 Rust具有与C ++不同的范例。在大多数情况下,通常可以使用通用类型来解决问题。 如果您的问题确实适合使用特征对象解决,则可以参考OOP chapter in the book