Macro try_transmute_mut

#[macro_export]
macro_rules! try_transmute_mut {
    ($e:expr) => { ... };
}

Conditionally transmutes a mutable reference of one type to a mutable reference of another type of the same size and compatible alignment.

Note that while the value of the referent is checked for validity at runtime, the size and alignment are checked at compile time. For conversions which are fallible with respect to size and alignment, see the methods on TryFromBytes.

This macro behaves like an invocation of this function:

fn try_transmute_mut<Src, Dst>(src: &mut Src) -> Result<&mut Dst, ValidityError<&mut Src, Dst>>
where
    Src: FromBytes + IntoBytes + ?Sized,
    Dst: TryFromBytes + IntoBytes + ?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

try_transmute_mut! supports transmuting between Sized types, between unsized (i.e., ?Sized) types, and from a Sized type to an unsized type. 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:

# use zerocopy::try_transmute_mut;
# use core::mem::size_of_val; // Not in the prelude on our MSRV
let src: &mut [[u8; 2]] = &mut [[0, 1], [2, 3]][..];
let dst: &mut [u8] = try_transmute_mut!(src).unwrap();

assert_eq!(dst.len(), 4);
assert_eq!(dst, [0, 1, 2, 3]);
let dst_size = size_of_val(dst);
assert_eq!(src.len(), 2);
assert_eq!(size_of_val(src), dst_size);

Examples

Transmuting between Sized types:

# use zerocopy::*;
// 0u8 → bool = false
let src = &mut 0u8;
assert_eq!(try_transmute_mut!(src), Ok(&mut false));

// 1u8 → bool = true
let src = &mut 1u8;
 assert_eq!(try_transmute_mut!(src), Ok(&mut true));

// 2u8 → bool = error
let src = &mut 2u8;
assert!(matches!(
    try_transmute_mut!(src),
    Result::<&mut bool, _>::Err(ValidityError { .. })
));

Transmuting between unsized types:

# use {zerocopy::*, zerocopy_derive::*};
# type u16 = zerocopy::byteorder::native_endian::U16;
# type u32 = zerocopy::byteorder::native_endian::U32;
#[derive(KnownLayout, FromBytes, IntoBytes, Immutable)]
#[repr(C)]
struct SliceDst<T, U> {
    t: T,
    u: [U],
}

type Src = SliceDst<u32, u16>;
type Dst = SliceDst<u16, bool>;

let mut bytes = [0, 1, 0, 1, 0, 1, 0, 1];
let src = Src::mut_from_bytes(&mut bytes).unwrap();

assert_eq!(src.t.as_bytes(), [0, 1, 0, 1]);
assert_eq!(src.u.len(), 2);
assert_eq!(src.u.as_bytes(), [0, 1, 0, 1]);

let dst: &Dst = try_transmute_mut!(src).unwrap();

assert_eq!(dst.t.as_bytes(), [0, 1]);
assert_eq!(dst.u, [false, true, false, true, false, true]);