Description
We'd like to provide support for:
- constructing DSTs in-place
- 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), mapSelf
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.