Proper way write to [MaybeUninit<u8>; CONST] via &mut [u8]

Constraints that I have:

algorithm to encode one byte encoding to utf-8:

pub fn decode_to_utf8_without_replacement(
    &mut self,
    src: &[u8],
    dst: &mut [u8],
    last: bool
) -> (DecoderResult, usize, usize)

Output type ArrayString it is just [MaybeUninit<u8>; CONST] + length.

How can I combine them together?

Is any problem with:

    let mut array_str: [MaybeUninit<u8>; 20] = unsafe { MaybeUninit::uninit().assume_init() };
    let slice: &mut [MaybeUninit<u8>] = &mut array_str;
    let (_, _, nwriten) = 
     decode_to_utf8_without_replacement(input, unsafe { mem::transmute(slice) }, ...);

UB or something?

1 Like

Please don't transmute pointer-like types. Transmutation of indirection is almost always wrong. It's not transitive: if transmute::<T, U>(t) is valid, it doesn't mean that transmute::<&T, &U>(&t) is also valid (nor the other way around).

There's also the actual/bigger problem here: initialization. slice points to uninitialized bytes, but creating a mutable reference to a type means that the value must be initialized. A &mut [u8] is not write-only, it can also be read from, and it absolutely requires that the byte range it's pointing to is initialized. By lying to the compiler via the transmute, you are causing insta-UB.

2 Likes

It is UB, and it can't be done. Use a 0-initialized buffer instead of uninitialized.

The function decode_to_utf8_without_replacement would have to be changed to take dst: &mut [MaybeUninit<u8>] instead.

This is from encoding_rs crate which has a very C-like API.

1 Like