Unsizing Casts and DST Construction
- Dominant language
- Rust
- Stars
- 2.6k
- Forks
- 179
- Avg merge
- 1d 19h
- Merged PRs (30d)
- 29
Description
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`](https://doc.rust-lang.org/stable/std/marker/trait.Unsize.html)/[`CoerceUnsize`](https://doc.rust-lang.org/stable/std/ops/trait.CoerceUnsized.html); e.g.:
```rust
// SAFETY: It is sound to cast `&T` to `&U`.
unsafe trait Unsize {
fn cast(s: PtrInner<'_, Self>) -> PtrInner<'_, U> { ... }
}
```
We can provide a blanket impl for arrays:
```rust
unsafe impl Unsize<[T]> for [T; N] {}
```
...and even a user-facing macro for dyn trait implementations:
```rust
#[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
for
T
$(where $( $x: $( $y $(+ $z )* )? ),+)?
{}
}
}
impl_unsize_for_trait!( AsRef => 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`](https://doc.rust-lang.org/stable/std/marker/trait.Unsize.html) 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.:
```rust
trait Resize {
type Sized: Unsize;
}
```
This would permit slice DSTs to be initialized naturally by type projecting through `ReSize`. For example, given this:
```rust
#[derive(Unsize)]
struct Concrete {
t: [T],
}
```
...we'd emit the sized counterpart:
```rust
ConcreteSized {
t: <[T] as Foo>::Sized;
}
```
...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.:
```rust
let s = 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
- https://github.com/rust-lang/rust/issues/86935
Contributor guide
Assessment
This issue has not been assessed yet.