struct D<T:?Sized>{
i:Box<T>
}
trait A{
fn show(&self){
println!("trait A");
}
}
impl A for i32{
fn show(&self){
println!("i32");
}
}
fn test(_:D<dyn A>){}
fn test2(v:Box<dyn A>){}
fn main(){
let c = D{i:Box::new(1)};
let coerced:D<dyn A> = D{i:Box::new(1)}; // ok
test(coerced); // ok
test(c); // #1 error
let c1 = Box::new(1);
let coerced_1:Box<dyn A> = Box::new(1); // ok
test2(c1); // #2 ok
test2(coerced_1); // ok
test(D{i:Box::new(1)}); // #3 ok
}
T to dyn U, when T implements U + Sized, and U is object safe.
Foo<..., T, ...> to Foo<..., U, ...>, when:
Foo is a struct.
T implements Unsize<U>.
The last field of Foo has a type involving T.
If that field has type Bar<T>, then Bar<T> implements Unsized<Bar<U>>.
T is not part of the type of any other fields.
i32 can be coerced to dyn A because i32 implements A + Sized,
The following coercions are built-ins and, if T can be coerced to U with one of them, then an implementation of Unsize<U> for T will be provided:
So Unsize<dyn A> for i32 will be provided. So, all bullets are satisfied for D<i32> and D<dyn A>. As the contrast, #1 is an error while #2 is ok. We must first coerce D<i32> to D<dyn A> and use the result to call test, or we should directly use D{i:Box::new(1)} as the argument to call test. Unlike Box, we can use it to call test2 in any way.
Why the argument of type D<i32> cannot be coerced to D<dyn A> in the function call like that Box<i32> is coerced to Box<dyn A>?
i32 implements Unsize<dyn A>, but the last field has type Box<T>, and Box<i32> does not implement Unsize<Box<dyn A>>. (Instead, it implements CoerceUnsized<Box<dyn A>>.) If you want to coerce your user-defined type to an unsized value, it must store the T by-value, and the whole object must be placed behind a Box or similar (Rust Playground):
struct D<T: ?Sized> {
i: T,
}
trait A {
fn show(&self) {
println!("trait A");
}
}
impl A for i32 {
fn show(&self) {
println!("i32");
}
}
fn main() {
let c: Box<D<i32>> = Box::new(D { i: 1 });
let coerced: Box<D<dyn A>> = c; // ok
coerced.i.show(); // ok
}
Structs Foo<..., T, ...> implement Unsize<Foo<..., U, ...>> if all of these conditions are met:
Box<i32> and Box<dyn A> is that case. If D<i32> couldn't be coerced to D<dny A> as you said, then let coerced:D<dyn A> = D{i:Box::new(1)}; would be an error.
Box<T> is only allowed to do that since it explicitly implements the unstable CoerceUnsized trait.
In your D<dyn A> example, the D<i32> is not actually coerced to a D<dyn A>. Instead, the Box<i32> is coerced to a Box<dyn A> before it is written to the i field. To illustrate, with your original definitions (Rust Playground):
fn main() {
// ok:
{
let b: Box<i32> = Box::new(1);
let b_coerced: Box<dyn A> = b;
let coerced: D<dyn A> = D { i: b_coerced };
}
// error:
{
let b: Box<i32> = Box::new(1);
let c: D<i32> = D { i: b };
let coerced: D<dyn A> = c;
}
}