The following MyBox struct is setup extremely similarly to Box, and yet it does not work with a vector of trait objects in the struct Vec<MyBox<dyn MyTrait>>. However a vector of trait objects in a Box works just fine, Vec<Box<dyn MyTrait>>.
The error is
expected struct
mybox::MyBox<dyn mybox::MyTrait>
found structmybox::MyBox<mybox::MyStruct>
What is it about Box that allows it to properly type check? Does Box implement a trait that allows it to resolve?
use std::ptr::NonNull;
trait MyTrait {}
struct MyStruct {}
impl MyTrait for MyStruct {}
struct MyBox<T: ?Sized> {
value: NonNull<T>
}
impl<T> MyBox<T> {
pub fn new(value: T) -> MyBox<T> {
unsafe {
MyBox { value: NonNull::new_unchecked(Box::into_raw(Box::new(value))) }
}
}
}
/// Works just fine
fn main_box() {
let mut v: Vec<Box<dyn MyTrait>> = Vec::new();
let x: Box<MyStruct> = Box::new(MyStruct {});
v.push(x);
}
/// Has type error
fn main_mybox() {
let mut v: Vec<MyBox<dyn MyTrait>> = Vec::new();
let x: MyBox<MyStruct> = MyBox::new(MyStruct {});
v.push(x);
}
I have tried mimicking the structure of Box<T> but there must be a trait that it implements that I am missing.
>Solution :
MyStruct and dyn MyTrait are different types, what allows coercion from one to the other for a Box is the CoerceUnsized implementation of Box<T, A> to Box<U, A> which you’re lacking.
#![feature(coerce_unsized, unsize)]
use std::marker::Unsize;
use std::ops::CoerceUnsized;
impl<T, U> CoerceUnsized<MyBox<U>> for MyBox<T>
where
T: Unsize<U> + ?Sized,
U: ?Sized {}
As you can see that requires 2 features to implement at the moment so you have to run a nightly compiler to use it.
If you have to use a stable compiler you could do the coercion on the Box and turn that into MyBox<dyn MyTrait> afterwards:
impl<T: ?Sized> From<Box<T>> for MyBox<T> {
fn from(b: Box<T>) -> Self {
unsafe {
MyBox {
value: NonNull::new_unchecked(Box::into_raw(b)),
}
}
}
}
/// Has type error
fn main_mybox() {
let mut v: Vec<MyBox<dyn MyTrait>> = Vec::new();
let b: Box<dyn MyTrait> = Box::new(MyStruct {}); // coercion happens here
let x: MyBox<dyn MyTrait> = b.into();
v.push(x);
}