Need workaround for Rust memory model to read potentially padding bytes of the enum

⚓ Rust    📅 2026-05-11    👤 surdeus    👁️ 4      

surdeus

I have an enum with variants of different size. I'd really like (for performance reasons in very hot code) to read fist N bytes of the variant's contents regardless of whether particular enum variant uses those bytes or not.

The problem is that there doesn't seem to be an ergonomic way to do that within Rust memory model. Adding explicit padding fields does work, but makes things very ugly.

I though of workaround by copying value with potentially uninitialized contents over initialized memory region, but looks like according to current Rust memory model override removes "initialized" status despite not making any sense from hardware point of view.

Something like this:

#![expect(dead_code)]

use std::mem::MaybeUninit;
use std::{ptr, slice};

#[derive(Copy, Clone, Debug)]
#[repr(C, u8)]
enum BadEnum {
    Small(u8),
    Large(u64),
}

#[inline(never)]
fn copy_enum_to_memory(mem: &mut MaybeUninit<BadEnum>, value: BadEnum) {
    mem.write(value);
}

fn main() {
    let mut mem = MaybeUninit::<BadEnum>::zeroed();

    let value = BadEnum::Small(42);
    copy_enum_to_memory(&mut mem, value);

    let initialized = unsafe { mem.assume_init() };

    let bytes = unsafe {
        slice::from_raw_parts(
            ptr::from_ref(&initialized).cast::<u8>(),
            std::mem::size_of::<BadEnum>(),
        )
    };

    let checksum: u32 = bytes.iter().map(|&b| b as u32).sum();
    println!("Checksum: {checksum}");
    println!("No UB found");
}

Right now it is UB:

error: Undefined Behavior: reading memory at alloc191[0x1..0x2], but memory is uninitialized at [0x1..0x2], and this operation requires initialized memory
  --> src/main.rs:33:44
   |
33 |     let checksum: u32 = bytes.iter().map(|&b| b as u32).sum();
   |                                            ^ Undefined Behavior occurred here
   |
   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
   = note: stack backtrace:
           0: main::{closure#0}
               at src/main.rs:33:44: 33:45
           1: std::iter::adapters::map::map_fold::<&u8, u32, u32, {closure@src/main.rs:33:42: 33:46}, {closure@<u32 as std::iter::Sum>::sum<std::iter::Map<std::slice::Iter<'_, u8>, {closure@src/main.rs:33:42: 33:46}>>::{closure#0}}>::{closure#0}
               at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/adapters/map.rs:88:28: 88:34
           2: <std::slice::Iter<'_, u8> as std::iter::Iterator>::fold::<u32, {closure@std::iter::adapters::map::map_fold<&u8, u32, u32, {closure@src/main.rs:33:42: 33:46}, {closure@<u32 as std::iter::Sum>::sum<std::iter::Map<std::slice::Iter<'_, u8>, {closure@src/main.rs:33:42: 33:46}>>::{closure#0}}>::{closure#0}}>
               at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/slice/iter/macros.rs:279:27: 279:85
           3: <std::iter::Map<std::slice::Iter<'_, u8>, {closure@src/main.rs:33:42: 33:46}> as std::iter::Iterator>::fold::<u32, {closure@<u32 as std::iter::Sum>::sum<std::iter::Map<std::slice::Iter<'_, u8>, {closure@src/main.rs:33:42: 33:46}>>::{closure#0}}>
               at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/adapters/map.rs:128:9: 128:50
           4: <u32 as std::iter::Sum>::sum::<std::iter::Map<std::slice::Iter<'_, u8>, {closure@src/main.rs:33:42: 33:46}>>
               at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/traits/accum.rs:52:17: 56:18
           5: <std::iter::Map<std::slice::Iter<'_, u8>, {closure@src/main.rs:33:42: 33:46}> as std::iter::Iterator>::sum::<u32>
               at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/traits/iterator.rs:3674:9: 3674:23
           6: main
               at src/main.rs:33:25: 33:62

Uninitialized memory occurred at alloc191[0x1..0x2], in this allocation:
alloc191 (stack variable, size: 16, align: 8) {
    00 __ __ __ __ __ __ __ 2a __ __ __ __ __ __ __ │ .░░░░░░░*░░░░░░░
}

I found that LLVM has freeze instruction, but there doesn't seem to be a way to use it from Rust. Ideally I'd like to read uninitialized bytes as zeroes, but as the last resort returning any value will do.

The goal is to do this ergonomically: not changing enum variants and not changing enum variant instantiation.

Is this really not possible today at all even on nightly?

1 post - 1 participant

Read full topic

🏷️ Rust_feed