Writing into Uninitialized Buffers in Rust

Rust vs C: Mental Model of Uninitialized Memory

  • Several comments contrast “it never occupies my mind in C” with “I must constantly think about it in Rust.”
  • In C, it’s common to allocate a buffer, hand it to read(), and treat it as fine as long as you don’t read past what was written.
  • In Rust, simply creating a reference (&mut [T]) to uninitialized bytes can be UB, so you must avoid references and use raw pointers or MaybeUninit, which feels like compiler-internals leaking into user code.

Vec, Slices, and Undefined Behaviour

  • Code like Vec::with_capacity, set_len, then as_mut_slice() and copy_to_slice() over uninitialized elements is discussed as UB or at least “library UB” and fragile.
  • There’s an ongoing language-level debate about whether just creating a reference to invalid data is UB (“recursive validity for references”) or only becomes UB if actually read.
  • Tools: Valgrind can catch some bad memory reads, but not UB at the language level; Miri or sanitizers are needed for Rust UB detection.

MaybeUninit, Spare Capacity, and API Pain

  • MaybeUninit plus Vec::spare_capacity_mut() is seen as the “correct” modern pattern: get &mut [MaybeUninit<T>], write into it, then set_len().
  • Many conversions ([MaybeUninit<T>] -> [T]) remain unstable, so people duplicate std implementations via unsafe casts.
  • I/O traits like Read predate MaybeUninit, so they take &mut [u8], forcing awkward transmute-based wrappers or double-buffer designs.

Proposed Language/Library Improvements

  • Several suggest language-level “write-only references” to express “this may be uninitialized; you may only write.”
  • Others want a “freeze” intrinsic: reading uninitialized values yields a stable but unspecified value instead of UB, at least for primitives or I/O buffers.
  • Rust RFCs on freeze and related traits exist, but commenters note subtle interactions with optimizations, paging (MADV_FREE), and security (Heartbleed-style leaks).

Performance vs Zeroing and Security Considerations

  • Zeroing all buffers is not always “negligible”: large or many buffers in tight loops can see big slowdowns; OS tricks like demand-zero pages or calloc can matter.
  • Reusing zeroed buffers is one workaround, but ownership patterns (threads, allocators) can complicate reuse.
  • Several emphasize that reading uninitialized bytes is a real security risk (secrets, pointers, ASLR leakage), so Rust’s strictness is intentional; the problem is ergonomic, not conceptual.

Unsafe vs Dropping to C

  • Some suggest using small C snippets for these hot paths, but others argue unsafe Rust is usually easier, more portable (WASM, cross-compiling), and still checkable by tools like Miri.
  • There’s recurring confusion over Rust’s goal: it isn’t “safety instead of performance,” but “safety with C-like performance,” enabled by a narrow, explicit unsafe escape hatch.