简体   繁体   中英

What does it mean to implement a trait for another trait?

I read this answer but I'm still confused.

  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.

You can't declare dyn A but you can declare &dyn A because dyn A is a trait object type while &dyn A is a pointer to an instance of type T that implements A .

Historically, a trait could be used as a type and a trait. For instance, these both work:

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

Basically, dyn A is just a sugar over A to make it clearer that it is meant to be used as a trait object type. You don't implement a trait for another trait. You implement a trait for another trait object type .

&dyn A is a pointer instance to an instance of type T that implements A and a virtual method table (vtable), which contains all the baggage of methods of A that T implements. This vtable lookup is necessary when an instance of type T later calls A 's implementation at runtime.

Therefore, dyn A is an unsized type while &dyn A is a pointer with a known size.

Trait object of type dyn A must be cast from a pointer to be used as a concrete type that implements A . For example, in the code example, i32 can be cast to a 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();
}

or it can be coerced by a function:

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

Because traits are dynamically sized types (DSTs), to use them as trait objects, we must put them behind some kind of pointer, like &dyn A or Box<dyn A> so it can point to a variable-sized value and access the vtable to call the implemented methods.

See also: What makes something a “trait object”?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM