google / google/zerocopy

Validate transmutation size using `const` code

Open
#239 0 comments 0 reactions 0 assignees View on GitHub
compatibility-breaking
Dominant language
Rust
Stars
2.6k
Forks
179
Avg merge
1d 19h
Merged PRs (30d)
29

Description

As discussed [here](https://github.com/google/zerocopy/issues/159#issue-1588293938) and [here](https://github.com/google/zerocopy/pull/183#discussion_r1208678157), it would be great if we could replace `transmute!` with an equivalent `transmute` function that performs size verification using `const` code. This would simplify the implementation, and would allow `transmute` to be used in type-generic contexts (`transmute!` can only be called in a context in which all types are concrete). However, due to limitations with const generics, errors can only be reported at monomorphization time. This causes some problems:
- There's no way to "bubble up" the size equality requirement since it's not actually expressed in the type system
- Type-generic APIs will never fail - they can only fail when they are used in a concrete context; this is true even across crate boundaries, meaning that it would be easy to accidentally publish a buggy API
- Thanks to https://github.com/rust-lang/rust/issues/112301, code that generates errors when compiled with `cargo build` would not generate errors when compiled with `cargo check`

There are a few ways that we could lift this information into the type system and avoid these issues, but all of them rely on unstable features:

## [associated_const_equality](https://github.com/rust-lang/rust/issues/92827)

```rust
#![feature(associated_const_equality)]

unsafe trait MaybeTransmutableFrom {
const IS_TRANSMUTABLE: bool;
}

unsafe impl MaybeTransmutableFrom for U {
const IS_TRANSMUTABLE: bool = {
assert!(core::mem::size_of::() == core::mem::size_of::());
true
};
}

unsafe trait TransmutableFrom {}

unsafe impl TransmutableFrom for U where U: MaybeTransmutableFrom {}
```

## [generic_const_exprs](https://github.com/rust-lang/rust/issues/76560)

```rust
#![feature(generic_const_exprs)]

unsafe trait MaybeTransmutableFrom {
const IS_TRANSMUTABLE: bool;
}

unsafe impl MaybeTransmutableFrom for U {
const IS_TRANSMUTABLE: bool = {
assert!(core::mem::size_of::() == core::mem::size_of::());
true
};
}

unsafe trait TransmutableFrom {}

unsafe impl TransmutableFrom for U
where
U: MaybeTransmutableFrom,
(): Bool<{ U::IS_TRANSMUTABLE }>,
{
}

trait Bool {}
impl Bool for () {}
```

Alternatively:

```rust
#![feature(generic_const_exprs)]

unsafe trait MaybeTransmutableFrom {}

unsafe impl
MaybeTransmutableFrom<{ core::mem::size_of::() == core::mem::size_of::() }, T> for U
{
}

unsafe trait TransmutableFrom {}

unsafe impl TransmutableFrom for U where U: MaybeTransmutableFrom {}
```

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.