Skip to content

Unsizing Casts and DST Construction #2490

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
jswrenn opened this issue Apr 8, 2025 · 0 comments
Open

Unsizing Casts and DST Construction #2490

jswrenn opened this issue Apr 8, 2025 · 0 comments

Comments

@jswrenn
Copy link
Collaborator

jswrenn commented Apr 8, 2025

We'd like to provide support for:

  1. constructing DSTs in-place
  2. casting sized pointers to unsized pointers

Casting and Unsize

We're confident the first step is polyfilling Unsize/CoerceUnsize; e.g.:

// SAFETY: It is sound to cast `&T` to `&U`.
unsafe trait Unsize<U: ?Sized> {
    fn cast(s: PtrInner<'_, Self>) -> PtrInner<'_, U> { ... }
}

We can provide a blanket impl for arrays:

unsafe impl<T, const N: usize> Unsize<[T]> for [T; N] {}

...and even a user-facing macro for dyn trait implementations:

#[macro_export]
macro_rules! impl_unsize_for_trait {
    (
        $(< $( $a:tt $(: $b:tt $(+ $c:tt )* )? ),+ >)?
        $trait:path
        for $t:ident 
        $(where $( $x:tt: $($y:tt $(+ $z:tt)* )? ),+)?
    ) => {
        unsafe impl<
            $t: ?Sized + $trait,
            $($( $a: ?Sized $(+ $b $(+ $c )* )? ),+ )?
        >
            Unsize<dyn $trait>
        for
            T
        $(where $( $x: $( $y $(+ $z )* )? ),+)?
        {}
    }
}


impl_unsize_for_trait!(<U> AsRef<U> => T);
impl_unsize_for_trait!(Any => T );
impl_unsize_for_trait!(Send => T);

For user-defined ADTs, we would provide #[derive(Unsize)], which:

  • On types that can be generically unsized (see Unsize doc bullet about structs), map Self to its unsized alternative.
  • For types that cannot be generically unsized, generate an unsized counterpart.

Blockers

  • We cannot actually implement casts to trait objects, because KnownLayout is not capable of representing the metadata type of trait objects.
  • We could implement casts to slice DSTs, but is this too much a foot-gun without in-place construction?

DST Construction

We think we can achieve this by providing a mapping from unsized types to their sized counterparts; e.g.:

trait Resize {
    type Sized<const N: usize>: Unsize<Self>;
}

This would permit slice DSTs to be initialized naturally by type projecting through ReSize. For example, given this:

#[derive(Unsize)]
struct Concrete<T> {
    t: [T],
}

...we'd emit the sized counterpart:

ConcreteSized<const N: usize, T> {
    t: <[T] as Foo>::Sized<N>;
}

...and implementations of Unsize and Resize relating the two.

Given this, a DST can be constructed naturally on the stack by using the Resize projection; e.g.:

let s = <Concrete<_> as Resize>::Sized {
    t: [1, 2, 3]
};
let u = s.cast_unsized();

This looks hokey, but that projection is something that could be performed automatically by a dst! macro.

Blockers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant