id: safety-03 original_id: P.UNS.SAS.03 level: P impact: CRITICAL clippy: uninit_assumed_init
Do Not Expose Uninitialized Memory in Public APIs
Summary
Public APIs must never return or expose uninitialized memory to callers.
Rationale
Reading uninitialized memory is undefined behavior in Rust. Safe code should never be able to access uninitialized memory through your API.
Bad Example
// DON'T: Expose uninitialized memory
pub struct Buffer {
data: [u8; 1024],
len: usize,
}
impl Buffer {
pub fn new() -> Self {
// BAD: data is uninitialized
unsafe {
Self {
data: std::mem::MaybeUninit::uninit().assume_init(),
len: 0,
}
}
}
// BAD: Returns reference to potentially uninitialized data
pub fn as_slice(&self) -> &[u8] {
&self.data[..self.len] // What if len > initialized portion?
}
}
Good Example
use std::mem::MaybeUninit;
// DO: Use MaybeUninit properly and only expose initialized data
pub struct Buffer {
data: Box<[MaybeUninit<u8>; 1024]>,
len: usize, // Invariant: data[0..len] is initialized
}
impl Buffer {
pub fn new() -> Self {
Self {
// MaybeUninit doesn't require initialization
data: Box::new([MaybeUninit::uninit(); 1024]),
len: 0,
}
}
pub fn push(&mut self, byte: u8) {
if self.len < 1024 {
self.data[self.len].write(byte);
self.len += 1;
}
}
// Only returns initialized portion
pub fn as_slice(&self) -> &[u8] {
// SAFETY: self.len bytes are initialized (invariant)
unsafe {
std::slice::from_raw_parts(
self.data.as_ptr() as *const u8,
self.len
)
}
}
}
impl Drop for Buffer {
fn drop(&mut self) {
// Only drop initialized elements
// For u8 this is a no-op, but important for Drop types
}
}
Patterns for Uninitialized Memory
// Pattern 1: MaybeUninit for delayed initialization
let mut value: MaybeUninit<ExpensiveType> = MaybeUninit::uninit();
initialize_expensive(&mut value);
let value = unsafe { value.assume_init() };
// Pattern 2: Vec::with_capacity for growable buffers
let mut vec = Vec::with_capacity(100);
// vec.len() is 0, capacity is 100
// No uninitialized memory is accessible
// Pattern 3: Box::new_uninit (nightly)
let mut boxed = Box::<[u8; 1024]>::new_uninit();
boxed.write([0u8; 1024]);
let boxed = unsafe { boxed.assume_init() };
Checklist
- Does my API ever return references to uninitialized memory?
- Are length/capacity invariants properly maintained?
- Is MaybeUninit used instead of transmute for uninitialized data?
Related Rules
mem-06: Use MaybeUninitfor uninitialized memory safety-01: Panic safety with partial initialization
你可能感兴趣的文章
Do Not Expose Raw Pointers in Public APIs
Unsafe Code Authors Must Verify Safety Invariants
Ensure Consistent Data Layout for Custom Types
Add Safety Section in Docs for Public Unsafe Functions
Do Not Use Union Variants Across Different Lifetimes
Use MaybeUninit
Ensure C-ABI Compatibility for Strings Between Rust and C
Do Not Dereference Pointers Cast to Misaligned Types
Use Dedicated Opaque Type Pointers Instead of c_void for C Opaque Types