Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to implement a Box-like `MyBox` that supports trait objects like `Vec<MyBox<dyn MyTrait>>`

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 struct mybox::MyBox<mybox::MyStruct>

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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);
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading