macro_rules! transmute_ref {
($e:expr) => { ... };
}
Expand description
Safely transmutes a mutable or immutable reference of one type to an immutable reference of another type of the same size and compatible alignment.
This macro behaves like an invocation of this function:
fn transmute_ref<'src, 'dst, Src, Dst>(src: &'src Src) -> &'dst Dst
where
'src: 'dst,
Src: IntoBytes + Immutable + ?Sized,
Dst: FromBytes + Immutable + ?Sized,
align_of::<Src>() >= align_of::<Dst>(),
size_compatible::<Src, Dst>(),
{
...
}
The types Src
and Dst
are inferred from the calling context; they cannot
be explicitly specified in the macro invocation.
§Size compatibility
transmute_ref!
supports transmuting between Sized
types or between
unsized (i.e., ?Sized
) types. It supports any transmutation that preserves
the number of bytes of the referent, even if doing so requires updating the
metadata stored in an unsized “fat” reference:
let src: &[[u8; 2]] = &[[0, 1], [2, 3]][..];
let dst: &[u8] = transmute_ref!(src);
assert_eq!(src.len(), 2);
assert_eq!(dst.len(), 4);
assert_eq!(dst, [0, 1, 2, 3]);
assert_eq!(size_of_val(src), size_of_val(dst));
§Errors
Violations of the alignment and size compatibility checks are detected after the compiler performs monomorphization. This has two important consequences.
First, it means that generic code will never fail these conditions:
fn transmute_ref<Src, Dst>(src: &Src) -> &Dst
where
Src: IntoBytes + Immutable,
Dst: FromBytes + Immutable,
{
transmute_ref!(src)
}
Instead, failures will only be detected once generic code is instantiated with concrete types:
let src: &u16 = &0;
let dst: &u8 = transmute_ref(src);
Second, the fact that violations are detected after monomorphization means
that cargo check
will usually not detect errors, even when types are
concrete. Instead, cargo build
must be used to detect such errors.
§Examples
Transmuting between Sized
types:
let one_dimensional: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
let two_dimensional: &[[u8; 4]; 2] = transmute_ref!(&one_dimensional);
assert_eq!(two_dimensional, &[[0, 1, 2, 3], [4, 5, 6, 7]]);
Transmuting between unsized types:
#[derive(KnownLayout, FromBytes, IntoBytes, Immutable)]
#[repr(C)]
struct SliceDst<T, U> {
t: T,
u: [U],
}
type Src = SliceDst<u32, u16>;
type Dst = SliceDst<u16, u8>;
let src = Src::ref_from_bytes(&[0, 1, 2, 3, 4, 5, 6, 7]).unwrap();
let dst: &Dst = transmute_ref!(src);
assert_eq!(src.t.as_bytes(), [0, 1, 2, 3]);
assert_eq!(src.u.len(), 2);
assert_eq!(src.u.as_bytes(), [4, 5, 6, 7]);
assert_eq!(dst.t.as_bytes(), [0, 1]);
assert_eq!(dst.u, [2, 3, 4, 5, 6, 7]);
§Use in const
contexts
This macro can be invoked in const
contexts only when Src: Sized
and
Dst: Sized
.