google / google/zerocopy

Unsizing Casts and DST Construction

Open
#2,490 0 comments 1 reaction 0 assignees View on GitHub
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.