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 orMaybeUninit, which feels like compiler-internals leaking into user code.
Vec, Slices, and Undefined Behaviour
- Code like
Vec::with_capacity,set_len, thenas_mut_slice()andcopy_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
MaybeUninitplusVec::spare_capacity_mut()is seen as the “correct” modern pattern: get&mut [MaybeUninit<T>], write into it, thenset_len().- Many conversions (
[MaybeUninit<T>] -> [T]) remain unstable, so people duplicate std implementations viaunsafecasts. - I/O traits like
ReadpredateMaybeUninit, 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
freezeand 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
calloccan 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
unsafeRust 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
unsafeescape hatch.