Do not read uninitialized memory of any non-union type as a typed value [RUSTNOMICON-UNINIT].
This is sometimes referred to as transmuting or read-at-type.
Memory can remain uninitialized if it is not read as a type.
Reading from a union is covered by Do not read from union fields that may contain uninitialized bytes.
Calling std::mem::MaybeUninit::assume_init
[MAYBEUNINIT-DOC] or any of the following related functions is treated in the same manner as a typed read:
Calling any of these functions on memory that is not fully initialized is undefined behavior [RUST-REF-BEHAVIOR].
|
|
|
|
Rust’s memory model requires that all bytes must be initialized before being read as a typed value
[RUSTNOMICON-UNINIT] [FERROCENE-SPEC].
Reading uninitialized memory as a typed value is undefined behavior [RUST-REF-BEHAVIOR].
This guideline aligns with functional safety standards [ISO-26262]
[IEC-61508] and secure coding practices.
Memory must be properly initialized according to the requirements of the variable’s type [UCG-VALIDITY].
For example, a variable of reference type must be aligned, non-null, and point to valid memory.
Similarly, entirely uninitialized memory may have any content, while a bool must always be true or false.
Consequently, reading an uninitialized bool is undefined behavior.
|
|
|
|
|
This noncompliant example extracts a value of type u32 from uninitialized memory within a MaybeUninit<T> container,
which is undefined behavior.
undefined behavior
use std::mem::MaybeUninit;
fn main() {
// Reading uninitialized memory as a typed value is undefined behavior
let _x: u32 = unsafe { MaybeUninit::uninit().assume_init() }; // noncompliant
}
|
|
|
|
|
This compliant example creates an uninitialized variable x of type MaybeUninit<u64>.
The code calls the write function to write the value 42 to x.
The call to assume_init asserts that the value is initialized and extracts the value of type u64.
This call to assume_init is compliant with this rule because the memory of x has been properly initialized by the call to write(42).
This is the canonically safe pattern for using MaybeUninit.
miri
use std::mem::MaybeUninit;
fn main() {
let mut x = MaybeUninit::<u64>::uninit();
x.write(42);
// SAFETY: 'x' is fully initialized
let _val = unsafe { x.assume_init() }; // compliant
}
|
|
|
|
|
This noncompliant example creates a pointer from uninitialized memory.
Not all bit patterns are valid pointers for all operations (e.g., provenance rules) [UCG-VALIDITY].
As can be seen here, even the raw pointer type (*const T) has validity rules [RUST-REF-BEHAVIOR].
undefined behavior
use std::mem::MaybeUninit;
fn main() {
// Undefined behavior creating a pointer from uninitialized memory
let _p: *const u32 = unsafe { MaybeUninit::uninit().assume_init() }; // noncompliant
}
|
|
|
|
|
This noncompliant example creates a reference from uninitialized memory.
Creating a reference from arbitrary or uninitialized bytes is undefined behavior [RUST-REF-BEHAVIOR].
References must be valid, aligned, dereferenceable, and non-null [UCG-VALIDITY]. Uninitialized memory cannot satisfy these requirements.
undefined behavior
use std::mem::MaybeUninit;
fn main() {
// Reading an invalid reference is undefined behavior
let _r: &u32 = unsafe { MaybeUninit::uninit().assume_init() }; // noncompliant
}
|
|
|
|
|
This noncompliant example has undefined behavior because it creates an invalid reference.
The &u8 reference has stricter validity requirements than the raw pointer *const u8.
While ptr::dangling() produces a non-null, well-aligned pointer, it does not point to valid, allocated memory.
The code writes a dangling raw pointer into memory and then calls assume_init(),
asserting that this memory contains a valid reference.
However, a dangling pointer is never a valid reference—even if you never dereference it.
The mere existence of an invalid reference is undefined behavior.
undefined behavior
use std::mem::MaybeUninit;
use std::ptr;
fn create_ref() {
let mut uninit: MaybeUninit<&u8> = MaybeUninit::uninit();
unsafe {
// write non-null and aligned address.
(&raw mut uninit).cast::<*const u8>().write(ptr::dangling());
// Undefined behavior occurs when asserting 'uninit' is a valid reference.
let _init = uninit.assume_init(); // noncompliant
}
}
fn main() {
create_ref();
}
|
|
|
|
|
Array elements must individually be valid values.
This noncompliant example creates an uninitialized array of four u8 values.
The call to .assume_init asserting that the array is initialized is valid here because
an array of MaybeUninit<u8> can contain uninitialized bytes.
The call to std::mem::transmute reinterprets the [MaybeUninit<u8>; 4] as [u8; 4].
This is undefined behavior, because the bytes were never initialized.
Even though all bit patterns (0-255) are valid for the u8 type, the values must be initialized.
MaybeUninit<u8> can hold uninitialized memory — that’s its purpose.
u8 cannot hold uninitialized memory — all 8 bits must be defined.
The transmute performs a typed read that asserts the bytes are valid u8 values.
Reading uninitialized bytes as a concrete type is always undefined behavior.
undefined behavior
use std::mem::MaybeUninit;
fn main() {
let arr: [MaybeUninit<u8>; 4] = unsafe { MaybeUninit::uninit().assume_init() };
// Undefined behavior constructing an array of 'u8' from uninitialized memory.
let _a = unsafe { std::mem::transmute::<_, [u8; 4]>(arr) }; // noncompliant
}
|
|
|
|
|
This compliant example defines a C-layout struct with:
The variable buf is a fully, zero-initialized 8-byte buffer.
The first two bytes of buf are overwritten.
The byte buffer buf pointer is cast to a pointer to S.
The call to read_unaligned reads the struct without requiring alignment.
This example is compliant because:
All bytes are initialized (buffer was zero-initialized)
All fields have valid values (u8 and u32 accept any bit pattern)
Padding bytes don’t need to be any specific value
read_unaligned handles the alignment issue
miri
#[repr(C)]
#[derive(Debug)]
struct S {
a: u8,
b: u32,
}
fn main() {
let mut buf = [0u8; std::mem::size_of::<S>()];
buf[0] = 10;
buf[1] = 20; // writing padding is fine
let p = buf.as_ptr() as *const S;
// SAFETY: All fields are initialized (padding doesn't matter)
let s = unsafe { p.read_unaligned() }; // compliant
println!("{:?}", s);
}
|
|