I read this answer but I'm still confused.
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(); }
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
?
I can also declare impl B for &dyn A {}
, so why I need impl B for dyn A {}
? What's the use case?
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.