首页 > 解决方案 > 是否可以将特征对象转换为另一个特征对象?

问题描述

我尝试了以下代码:

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
  --> src/main.rs:34:9
   |
34 |     b = a;
   |         ^ expected trait `TraitB`, found trait `TraitA`
   |
   = note: expected struct `std::boxed::Box<dyn TraitB>`
              found struct `std::boxed::Box<dyn TraitA>`

我声明了两个特征和一个名为的类型MyType,并为MyType. 我创建了一个新的 trait 对象TraitAMyType我称之为a. 由于a也实现TraitB了,我认为它应该可以被转换为TraitB.

我还没有弄清楚这是否可能。如果是,我怎样才能将特征对象a转换为TraitB

在 C++ 中,我会使用类似的东西来std::dynamic_pointer_cast<TraitB>(a);达到同样的目的。

这是我可以使用横向转换的示例:我有一个结构,其中包含一些代表现实生活中的实体的数据:

struct MyType {
    a: i32,
    b: i32,
}

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

有趣的部分是get_final_value应该根据谁调用它而做出不同的响应。

标签: dynamiccastingrust

解决方案


另一种选择是创建一个同时使用TraitATraitB作为超特征并为每种类型提供强制转换的特征:

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

然后MyType实现它:

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

一旦你这样做了,你就可TraitC以为你Box和你的程序逻辑使用两者TraitATraitB一起使用。

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

fn test_a(a: &TraitA) {
    a.say_hello();
}
fn test_b(b: &TraitB) {
    b.say_hello();
}

fn main() {
    let c: Box<dyn TraitC> = Box::new(MyType {});

    TraitA::say_hello(&*c);
    TraitB::say_hello(&*c);

    c.as_trait_a().say_hello();
    c.as_trait_b().say_hello();

    test_a(c.as_trait_a());
    test_b(c.as_trait_b());

    let a: &dyn TraitA = c.as_trait_a();
    a.say_hello();
    let b: &dyn TraitB = c.as_trait_b();
    b.say_hello();
}

锈游乐场

如果A并且B确实属于一起,这更好地代表了这一点,并且如果您愿意,您仍然可以自由地单独使用它们。


推荐阅读