This guide starts with a forewarning: safe-transmute had many safety issues before this version,
which means that there is a chance of your dependent project facing undefined behavior. Migrating
to version 0.11 is recommended as soon as possible, even if it might lead to a sub-optimal solution.
The crate is now organized with the following major categories:
basecontains all baseline conversion functions. They are only protected against out of boundary access, like trying to create an 8-byte type from 7 bytes. However, they are still unsafe: any attempt of transmuting data to an invalid memory representation is still undefined behavior. Moreover, unaligned memory access is not prevented, and must be previously ensured by the caller.- The
trivialmodule introduces the concept of trivially transmutable, which statically ensures that any bit combination makes a valid value for a given type. Basically, if a typeTcan be filled with any arbitrary bits in its memory representation and still be valid, thenTis trivially transmutable. Most primitive types implement theTriviallyTransmutabletrait, as well as arrays of trivially transmutable types, but new types (such as repr-C structs) need tounsafe implit manually. Functions in this module are therefore safer than the baseline, but are still unsafe because they do not check for memory alignment. to_bytesenables the opposite operation of reintepreting values as bytes. They are usually safe unless when working with mutable slices, since they can break invariants of the source type.- The
boolmodule ensures safe transmutation of bytes to boolean values. - The
fullmodule is at the root of this crate. These are transmutation functions with enough checks to be considered safe to use in any circumstance. The operation may still arbitrarily return (recoverable) errors due to unaligned data or incompatible vector transmutation targets, but it will not eat your laundry, and helper functions are available to assist the programmer in making some use cases work.
Moreover, there are also two utility modules:
- The
guardmodule contains the Guard API, which imposes slice boundary restrictions in a conversion. - The
alignmodule is where alignment checks are implemented. - The
utilmodule provides some independent helper functions. You might not need these.
Generally, you are strongly advised to stick to the functions provided at the root of the crate.
These are re-exports from the full, bool, and to_bytes categories depending on their safety.
One of the major use cases of the crate is to grab a slice of bytes and reinterpret it as a slice of
another type T. This process is accompanied with a check for the source slice length so that it
makes some sense in the target type. If you expect any number of elements of the target type, use
transmute_many_permissive.
use safe_transmute::{Error, transmute_many_permissive};
let bytes = &[0x00, 0x01, 0x12, 0x24,
0x00]; // 1 spare byte
match transmute_many_permissive::<u16>(bytes) {
Ok(words) => {
assert_eq!(words,
[u16::from_be(0x0001), u16::from_be(0x1224)]);
},
Err(Error::Unaligned(e)) => {
// Copy needed, would otherwise trap on some archs
let words = e.copy();
assert_eq!(*words,
[u16::from_be(0x0001), u16::from_be(0x1224)]);
},
Err(e) => panic!("Unexpected error: {}", e),
}transmute_many_permissive is an alias for transmute_many with the permissive guard (PermissiveGuard)
as the guard type parameter G. If you expect at least 1 element, use transmute_many with the
SingleManyGuard as the guard type. If you expect at least one element and no extraneous bytes, use
transmute_many_pedantic, or transmute_many with PedanticGuard.
As you can see, we had to manually handle the case where the slice of bytes is not well aligned for
reading target data, such as from u8 to u16. If the slice's first element is not aligned for
reading u16s, the operation will just fail with the Unaligned error. The only way to move on from
here is to copy the data (this is provided by the error's copy method).
The good news is that this boilerplate was provided into the try_copy! macro. Here's how you'll
often be doing transmutations:
use safe_transmute::{transmute_many_permissive, try_copy};
let bytes = &[0x00, 0x01, 0x12, 0x24, 0x00];
let words = try_copy!(transmute_many_permissive::<u16>(bytes));
assert_eq!(&*words, &[u16::from_be(0x0001), u16::from_be(0x1224)]);You will also find to_bytes, mut, and bool variants of these functions. transmute_to_bytes turns any slice into
a slice of bytes. Functions of the _mut family work with mutable slices. transmute_bool checks whether all bytes
make valid boolean values.
You might have used safe-transmute's vector transmutation functions. Well, it turns out that they are
incredibly unsafe, and hard to get it right. This will be more complicated to migrate efficiently.
The new transmute_vec only works under very restricted conditions: the mem::align_of and mem::size_of between the
source and target element types must match. Otherwise, a full copy of the vector must be made.
use safe_transmute::{transmute_vec, try_copy};
let bytes = vec![0x00, 0x01, 0x12, 0x24, 0x00];
let words = try_copy!(transmute_vec::<_, u16>(bytes)); // !!! works, but will always copy
assert_eq!(&*words, &[u16::from_be(0x0001), u16::from_be(0x1224)]);Oftentimes, you'll just be avoiding vector transmutation entirely.
In order to avoid copies, you can allocate a vector of the target type T, transmute a mutable slice of the vector
into the source data type S, and write the data in there directly. This still requires both S and T to be
trivially transmutable in order to be within the compiler's safety guarantees.