I am having trouble understanding the following error:
// test.rs
struct A {
a: u32
}
trait B { }
impl B for A { }
struct C {
c: Vec<Box<dyn B>>,
}
fn test() {
let a = A {a: 1};
let a_vec = vec![Box::new(a)];
let c = C {
c: a_vec,
// c: vec![Box::new(a)],
};
}
Compile error:
mismatched types
expected trait object `dyn test::B`, found struct `test::A`
The error occurs on the line where I try to create C. What's interesting is that if I create C in the following way then it compiles alright.
let c = C {
c: vec![Box::new(a)],
};
Another way that would work is
let a_vec: Vec<Box<dyn B>> = vec![Box::new(a)];
let c = C {
c: a_vec,
};
One other experiment I did is to change the type of C.c to just Box instead of Vec<Box>, and then it compiles no matter how I initiate it.
Seems to me this might be some missing feature/bug of Rust's type inference regarding vector of trait objects? I am still very new to Rust so any idea on this is really appreciated!
This is all expected behaviour. A Box<A>
can be coerced into a Box<dyn B>
– this is called an unsized coercion, and it happens implicitly whenever the compiler knows that the target type is Box<dyn B>
. However, when just writing
let a_vec = vec![Box::new(a)];
the compiler doesn't know the item type of the vector, so it infers it from the expression on the right-hand side, meaning that a_vec
ends up with the type Vec<Box<A>>
.
There is no unsized coercion from Vec<Box<A>>
to Vec<Box<dyn B>>
. Converting Box<A>
to Box<dyn B>
simply means converting a pointer to a fat pointer on the stack, which is a cheap operation. Converting vectors of these elements is very different – it would require reallocating the whole vector and unsizing each element, which is a rather expensive operation, so it should never happen implicitly.
In all the versions that actually compile, the vector is created as a Vec<Box<dyn B>>
right from the get-go, since you tell the compiler that's the type you want.