是否可以将特征对象投射到另一个特征对象? [英] Is it possible to cast a trait object to another trait object?

查看:58
本文介绍了是否可以将特征对象投射到另一个特征对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试了以下代码:

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

特质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!(您好,A);
}
}

impl MyType的TraitB {
fn say_hello_from_b(& self){
println!(来自B的Hello);
}
}

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

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

我收到以下编译错误:

 错误[E0308]:类型不匹配的
-> src / main.rs:34:9
|
34 | b = a;
| ^预期特征`TraitB`,发现特征`TraitA`
|
=注意:预期的struct`std :: boxed :: Box< dyn TraitB>`
找到了struct`std :: boxed :: Box< dyn TraitA>`

我声明了两个特征和一个名为 MyType 的类型,并为<$ c实现了这两个特征$ c> MyType 。我创建了类型为 MyType 的新特征对象 TraitA ,我将其称为 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 的响应方式取决于调用者。




  • 我为什么不将类型分成两个不同的类型?:从技术上讲,按设计, a b 在一起,并不是说 get_final_value()使用这两个值来计算结果。


  • 为什么不使用泛型/静态调度?因为 MyType 只是一个例子。在实际情况下,我具有不同的结构,它们全部以不同的方式实现这两个特征。


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



解决方案

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

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

然后有 MyType 实现它:

  iType TraitC for MyType {
fn as_trait_a(& self)-> & dyn TraitA {
self
}
fn as_trait_b(& self)-> & dyn特性B {
self
}
}

这样,您就可以在 Box 中使用 TraitC ,并且在程序逻辑中同时使用 TraitA TraitB 一起。



主要示例显示了多种使用方式:

  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());

让a:& dyn TraitA = c.as_trait_a();
a.say_hello();
让b:& dyn特性B = c.as_trait_b();
b.say_hello();
}

铁锈操场



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

I tried the following code:

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();
}

I get the following compilation error:

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>`

I declared two traits and a type called MyType and implemented both traits for MyType. I created a new trait object TraitA of type MyType which I called a. Since a also implements TraitB, I thought it should be able to be casted as TraitB.

I haven't figured out if it's even possible. If it is, how can I cast trait object a into TraitB?

In C++, I would use something similar to std::dynamic_pointer_cast<TraitB>(a); for the same purpose.

Here's an example of a case where I could use lateral casting: I have a struct with some data inside that represents some real life entity:

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

Instances of this type can be used in at least two different parts of the code base. On both parts I need a behavior called get_final_value.

The interesting part is that get_final_value should respond differently depending on who called it.

  • Why don't I split the type into two different ones?: Technically, by design, a and b belong together, not to say that get_final_value() uses both values to compute the result.

  • Why not use generics/static dispatch? Because MyType is just one example. In the real case I have different structs, all of them implementing both traits in different ways.

  • Why not use Any trait? To be honest, I didn't know of it's existence until recently. I don't recall The Rust Programming Language mentioning it. Anyway, it seems you need to know the concrete type to do a cast from Any to that concrete type and then to the trait object.

解决方案

Another option is to create a trait that uses both TraitA and TraitB as supertraits and provides a cast to each type:

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

Then have MyType implement it:

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

Once you do that, you can use TraitC for your Box and your program logic that uses both TraitA and TraitB together.

Sample main to show various ways to use:

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

If A and B do truly belong together, this better represents that and still gives you the freedom to use them separately if you desire.

这篇关于是否可以将特征对象投射到另一个特征对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆