# Typic
Typic lets you transmute fearlessly.
Unless you give it your express permission, Typic prevents transmutes that are ***unstable***, ***unsafe***, and ***unsound***.
## Types of Transmutes
| Type | Traits |
| ------- | ---------- |
| Safe | `TransmuteInto`, `TransmuteFrom`
| Unsafe | `UnsafeTransmuteInto`, `UnsafeTransmuteFrom`
### Safe (e.g., `TransmuteInto`)
Neither transmuting nor using the transmuted value can invoke UB.
```rust
pub unsafe trait TransmuteInto<U, O = ()>: Sized
where
O: TransmuteOptions,
{
/// Reinterprets the bits of `self` as type `U`.
fn transmute_into(self) -> U;
}
```
### Unsafe (e.g., `UnsafeTransmuteInto`)
Either transmuting or using the transmuted value may invoke UB.
```rust
pub unsafe trait UnsafeTransmuteInto<U, O = ()>: Sized
where
O: UnsafeTransmuteOptions,
{
/// Reinterprets the bits of `from` as type `Self`.
unsafe fn unsafe_transmute_into(self) -> U;
}
```
## Transmute Options
**By default**, Typic ***statically forbids*** transmutes that are **unstable**, **unsafe**, or **unsound**, for both safe and unsafe transmutes. However, you may explicitly opt-in to neglecting these static guarantees with the (optional) `O` type parameter of `TransmuteInto` and `UnsafeTransmuteInto`.
Four options are available:
1. `neglect::Stability`
2. `neglect::Alignment`<sup>†</sup>
3. `neglect::Visibility`<sup>†</sup>
4. `neglect::Validity`<sup>†</sup>
<sup>†Only available for unsafe transmutations.</sup>
These options can be arbitrarily combined, e.g.:
```rust
...
where
T: UnsafeTransmuteInto<U, (neglect::Stability, neglect::Alignment)>
```
### `neglect::Stability`
By default, Typic ***statically requires*** that the layouts of the source and destination types are part of their API guarantee. Library authors indicate this for their types by implementing a marker trait. This trait should only be implementable for types where Rust makes guarantees about their layout (e.g., `#[repr(C)]`).
For example:
```rust
fn serialize<T, W>(ty : T, dst: W) -> std::io::Result<()>
where
T: TransmuteInto<[u8; size_of::<T>()], neglect::Stability>
{
...
}
```
This is the only option available for safe transmutations.
Typic will ***still*** reject transmutes between types where the layouts of the source or destination types are compiler UB (e.g., most `repr(Rust)` types).
### `neglect::Alignment`
By default, Typic ***statically requires*** that, when transmuting between references, the destination type does not have stronger alignment requirements than the source type.
This option is useful for implementing common fallible transmutations, like [bytemuck]'s [`try_cast_ref`] (See [***Case Study: Bytemuck***](#Implementing-bytemuck’s-try_cast_ref)).
This option is ***only*** available for ***unsafe*** transmutations, since dereferencing an unaligned pointer may invoke UB.
### `neglect::Visibility`
Typic assumes that if a field of a type isn't `pub`, the type might enforce invariants on its value. By default, Typic ***statically requires*** that all bytes in the destination type are marked `pub`.
If you have special knowledge about the type (e.g., because you're the author), you can opt-out of this guarantee by asserting `T: UnsafeTransmuteInto<U, neglect::Visibility>`.
This option is ***only*** available for ***unsafe*** transmutations, since calling methods on the transmuted reference may only be safe if the type's internal invariants are upheld.
### `neglect::Validity`
(Typic does not currently implement this, but will soon.)
By default, Typic only accepts transmutations for which all possible values of the source type are bit-valid values of the destination type. (This means no `u8 → bool` transmutes!)
If you have special knowledge about the value (e.g., because you've ensured at runtime that it's a bit-valid instance of the destination type), you can opt-out of this guarantee by asserting `T: UnsafeTransmuteInto<U, neglect::Visibility>`.
Typic will still reject transmutations that cannot possibly be valid for any value, e.g.:
```rust
#[typic::repr(C)] enum Foo { N = 24 }
#[typic::repr(C)] enum Bar { N = 42 }
let bar : Bar = (Foo::N).unsafe_transmute_into() // Compile error!
```
This option is ***only*** available for ***unsafe*** transmutations, since merely transmuting a bit-invalid value invokes UB.
## Case Studies
Typic can provide a flexible foundation for building safe abstractions that rely on transmutation. Here are some examples:
### Implementing [zerocopy]
[zerocopy]: https://crates.io/crates/zerocopy
[`AsBytes`]: https://docs.rs/zerocopy/0.2.*/zerocopy/trait.AsBytes.html
[`FromBytes`]: https://docs.rs/zerocopy/0.2.*/zerocopy/trait.FromBytes.html
Typic can easily express [zerocopy]'s foundational marker traits; e.g. [`FromBytes`] and [`AsBytes`]:
```rust
/// Indicates `Self` can be produced from an
/// appropriately-sized array of arbitrarily
/// initialized bytes.
pub trait FromBytes {}
impl<T> FromBytes for T
where
T: TransmuteFrom<[u8; size_of::<T>()]>
{}
/// Indicates `Self` can be converted into an
/// appropriately-sized array of arbitrarily
/// initialized bytes.
pub trait AsBytes {}
impl<T> AsBytes for T
where
T: TransmuteInto<[u8; size_of::<T>()]>
{}
```
(For accessibility, I've used const-generic features in the example above, but these traits are completely expressible on *stable* Rust with [`generic_array`](https://docs.rs/generic-array/*/generic_array/)).
### Implementing [bytemuck]'s [`try_cast_ref`]
[bytemuck]: https://docs.rs/bytemuck/*/bytemuck/
[`try_cast_ref`]: https://docs.rs/bytemuck/1.*/bytemuck/fn.try_cast_ref.html
In the example below, the presence of the `neglect::Alignment` option clearly communicates that we need to consider (and enforce at runtime) pointer alignment requirements:
```rust
fn try_cast_ref<'t, 'u, T, U>(src : &'t T) -> Option<&'u U>
where
&'t T: UnsafeTransmuteInto<&'u U, neglect::Alignment>
{
let dst_is_stricter = align_of::<U>() > align_of::<T>();
let src_is_unaligned = (src as *const T as usize) % align_of::<U>() != 0;
if dst_is_stricter && src_is_unaligned { return None; }
Some(unsafe { src.unsafe_transmute_into() })
}
```