This question already has answers here:
Why doesn't Rust support trait object upcasting?

(3个答案)


去年关闭。




我尝试了以下代码:

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实现了这两个特征。我创建了一个类型为TraitA的新特征对象MyType,我将其称为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的响应方式取决于谁调用它。
  • 为什么不将类型分成两个不同的类型?:从技术上讲,从设计上来说,ab属于同一类,并不是说get_final_value()使用这两个值来计算结果。
  • 为什么不使用泛型/静态调度?因为MyType只是一个示例。在实际情况下,我有不同的结构,它们都以不同的方式实现了这两个特征。
  • 为什么不使用Any特性?老实说,直到最近我才知道它的存在。我不记得Rust编程语言提到过它。无论如何,似乎您需要了解具体类型,才能从Any转换为该具体类型,然后再转换为trait对象。
  • 最佳答案

    另一个选择是创建一个使用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();
    }
    

    Rust Playground

    如果AB确实真正属于在一起,则可以更好地表示这一点,并且仍然可以让您自由使用它们(如果需要)。

    关于dynamic - 是否可以将特征对象转换到另一个特征对象?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/55895583/

    10-14 12:44