map_box_from

Crates.iomap_box_from
lib.rsmap_box_from
version0.1.1
created_at2025-10-07 15:52:18.771959+00
updated_at2025-10-07 16:04:42.296463+00
descriptionAdds `Box`-ed versions of `From` and `Into` traits - allowing implementations for unsized type parameters and following looser guidelines.
homepage
repository
max_upload_size
id1871841
size10,588
(ArsenalAlex108)

documentation

README

map_box_from

Adds Box-ed versions of From and Into traits - allowing implementations for unsized type parameters and following looser guidelines.

Reasoning

Due to the invasiveness of the identity conversion implementation impl<T> From<T> for T and the lack of negative trait implementation, some conversion implementations like impl<T> From<T> for Unit or impl<T> From<T> for NewType<T> are currently impossible in stable Rust. Compromises will have to made to specify to the compiler that Self is not part of T, using one of the following methods:

  • Place an arbitrary bound on T and make Self not satisfy that bound, i.e. using a widely implemented trait like Debug or Unpin and unimplement them on Self
  • Define bespoke traits fitting the usecase

This crate chooses the second method and provide MapBoxFrom and MapBoxInto traits that instead operates on Box to allow the use of unsized type parameters. While this crate maps all existing Into implementations into MapBoxFrom implementations, unsized type parameters are untouched and users are free to add new blanket implementations while avoiding conflict with blanket implemetations in Sized land.

Note: TryMapBoxFrom and TryMapBoxInto have not been added due to some considerations about how their blanket implementations should be added.

Examples (taken from MapBoxFrom documentation)

Lifting Sized types to unsized land and recreating the identity conversion impl:

use std::ops::Deref as _;

// Private empty base trait only implemented for Self: DynSelf<Self>
trait DynSelf<T: ?Sized> {}
impl<T: ?Sized> DynSelf<T> for T {}

// Unsized wrapper around trait object.
#[repr(transparent)]
pub struct AsUnsized<T: ?Sized>(dyn DynSelf<T>);

// Extension trait for conversion from Sized into unsized wrapper.
pub trait IntoUnsized<T> {
    fn into_unsized(self) -> Box<AsUnsized<T>>;
}

impl<T> IntoUnsized<T> for T {
    fn into_unsized(self) -> Box<AsUnsized<Self>> {
        let boxed: Box<dyn DynSelf<T>> = Box::new(self);
        // Assert that the trait object version has the same size.
        debug_assert!(size_of_val(boxed.deref()) == size_of::<T>());        
        unsafe {
            let ptr = Box::into_raw(boxed);
            // This cast is safe since DynSelf<T> is only implemented by T.
            Box::from_raw(ptr as *mut AsUnsized<T>)
        }
    }
}

impl<T> AsUnsized<T> {
    // Downcasting is done via pointer casting - no vtables required.
    fn into_sized(self: Box<Self>) -> T {
        debug_assert!(size_of_val(self.deref()) == size_of::<T>());
        unsafe {
            let ptr = Box::into_raw(self);
            *Box::from_raw(ptr as *mut T)
        }
    }
}

// Example identity conversion impl
impl<T> MapBoxFrom<AsUnsized<T>> for AsUnsized<T> {
    fn map_box_from(value: Box<AsUnsized<T>>) -> Box<Self> {
        value
    }
}

let int = 16_i32;
let boxed_int = int.into_unsized();
debug_assert!(size_of_val(boxed_int.deref()) == size_of::<i32>());
// Calling the identity conversion on an unsized type:
let boxed_int = AsUnsized::<i32>::map_box_from(boxed_int);
let unboxed_int = boxed_int.into_sized();
debug_assert!(int == unboxed_int);
Commit count: 0

cargo fmt