# 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() }) } ```