8

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.

Izana
  • 2,537
  • 27
  • 33
  • 2
    also posted and answered in Rust forum: https://users.rust-lang.org/t/what-does-it-mean-to-implement-trait-for-trait/44031?u=jarvi-izana – Izana Jun 09 '20 at 06:17
  • update: an inlined, super detailed answer. https://users.rust-lang.org/t/what-does-it-mean-to-implement-trait-for-trait/44031/2?u=jarvi-izana – Izana Jun 09 '20 at 06:45

1 Answers1

5

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”?

Pandemonium
  • 7,724
  • 3
  • 32
  • 51
  • 1
    Thanks for the answer. I think it's better for you to explain `(*x).method_b();`. I totally agree with the `trait object type` explanation (I will give you thumb up for that). But for `impl B for dyn A {}` explanation. I found this https://users.rust-lang.org/t/what-does-it-mean-to-implement-trait-for-trait/44031/2?u=jarvi-izana is great. I'd like to share with you. – Izana Jun 09 '20 at 06:56