实现一个Trait给另一个Trait是什么意思?

8

我看了这个答案,但我还是很困惑。

  1. How do you interpret impl B for dyn A {}?

    trait A {
        fn method_a(&self) {
            println!("a");
        }
    }
    
    trait B {
        fn method_b(&self) {
            println!("b")
        }
    }
    
    impl B for dyn A {}
    
    impl A for i32 {}
    
    fn main() {
        let x: &dyn A = &10;
        x.method_b();
    }
    

    Playground

    I can understand impl A for i32 {} because i32 is a concrete type. dyn A is not a concrete type (unsized, can't pass by value), and you cannot declare a dyn A but you can only declare a &dyn A. Should I interpret

    // x.method_b();
    (*x).method_b();
    

    as *x is dyn A?

  2. I can also declare impl B for &dyn A {}, so why I need impl B for dyn A {}? What's the use case?

  3. Follow up: If I modify the code

    fn main() {
        let x: &dyn A = &10;
        // have a B trait object over dyn A since
        // dyn A implements B
        let y: &dyn B = x;  
    }
    

    It will fail and complain &dyn A is not &dyn B. I understand this is a reasonable complain but I provide the option for compiler to use impl B for dyn A {}. Apparently, the compiler doesn't consider that's an option.


2
该程序相关内容已发布并在 Rust 论坛中得到回答:https://users.rust-lang.org/t/what-does-it-mean-to-implement-trait-for-trait/44031?u=jarvi-izana - Izana
更新:一个内联的、超详细的答案。https://users.rust-lang.org/t/what-does-it-mean-to-implement-trait-for-trait/44031/2?u=jarvi-izana - Izana
1个回答

5

你不能声明 dyn A,但你可以声明 &dyn A,因为 dyn A 是特质对象的类型,而 &dyn A 是实现了 A 的类型为 T 的指针。

在历史上,特质可以用作类型和特质。例如,下面这两个都适用:

// Where B and A are traits
impl B for A {}
impl B for dyn A {}

dyn A实际上只是对A的一种简化表示,以更清晰地表明它被用作特质对象类型。 您不会为另一个特质实现特质。您会为另一个特质对象类型实现特质。

&dyn A是指向实现A的类型T的实例的指针实例,并带有虚拟方法表(vtable),其中包含T实现的A的所有方法的相关信息。当类型T的实例在运行时调用A的实现时,必须进行此vtable查找。

因此,dyn A是一个不定大小的类型,而&dyn A是带有已知大小的指针。

必须将类型为dyn A的特质对象从指针转换为可以用作实现A的具体类型。例如,在代码示例中,可以将i32强制转换为dyn A

impl B for dyn A {}

impl A for i32 {}

fn main() {
    let x: i32 = 10;
    (&x as &dyn A).method_a(); 
    (&x as &dyn A).method_b();
}

或者它可以被一个函数所强制转换:

fn dispatch(a: &dyn A) {
    a.method_b();
}

因为特质(Trait)是动态大小类型(DSTs),所以要将它们作为特质对象使用,必须将它们放在某种指针后面,例如&dyn ABox<dyn A>,这样它就可以指向可变大小的值并访问vtable以调用实现的方法。
另请参阅:什么使得某物成为“trait object”?

1
谢谢你的回答。我认为你最好解释一下 (*x).method_b();。我完全同意 trait object type 的解释(我会给你点赞)。但是对于 impl B for dyn A {} 的解释。我发现这个链接 https://users.rust-lang.org/t/what-does-it-mean-to-implement-trait-for-trait/44031/2?u=jarvi-izana 很棒。我想和你分享。 - Izana

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接