Files
Sprimo/skills/unsafe-checker/rules/mem-06-maybeuninit.md
2026-02-12 22:58:33 +08:00

3.8 KiB

id, original_id, level, impact, clippy
id original_id level impact clippy
mem-06 G.UNS.MEM.01 G HIGH uninit_assumed_init, uninit_vec

Use MaybeUninit for Uninitialized Memory

Summary

Use MaybeUninit<T> instead of mem::uninitialized() or mem::zeroed() when working with uninitialized memory.

Rationale

  • mem::uninitialized() is deprecated and unsound
  • mem::zeroed() is UB for types where zero is invalid (references, NonZero, bool)
  • MaybeUninit<T> clearly marks memory as potentially uninitialized
  • Compiler can optimize based on initialization state

Bad Example

// DON'T: Use deprecated uninitialized
fn bad_uninit<T>() -> T {
    unsafe { std::mem::uninitialized() }  // Deprecated, UB
}

// DON'T: Use zeroed for types where zero is invalid
fn bad_zeroed() -> &'static str {
    unsafe { std::mem::zeroed() }  // UB: null reference
}

fn bad_zeroed_bool() -> bool {
    unsafe { std::mem::zeroed() }  // UB: 0 might not be valid bool
}

// DON'T: Transmute to "initialize"
fn bad_transmute() -> [String; 10] {
    unsafe { std::mem::transmute([0u8; std::mem::size_of::<[String; 10]>()]) }
}

// DON'T: Set Vec length without initializing
fn bad_vec() -> Vec<String> {
    let mut v = Vec::with_capacity(10);
    unsafe { v.set_len(10); }  // Elements are uninitialized!
    v
}

Good Example

use std::mem::MaybeUninit;

// DO: Use MaybeUninit for delayed initialization
fn good_array() -> [String; 10] {
    let mut arr: [MaybeUninit<String>; 10] =
        unsafe { MaybeUninit::uninit().assume_init() };

    for (i, elem) in arr.iter_mut().enumerate() {
        elem.write(format!("item {}", i));
    }

    // SAFETY: All elements initialized above
    unsafe { std::mem::transmute::<_, [String; 10]>(arr) }
}

// DO: Use MaybeUninit with arrays (cleaner with array_assume_init)
fn good_array_nightly() -> [String; 10] {
    let mut arr: [MaybeUninit<String>; 10] =
        [const { MaybeUninit::uninit() }; 10];

    for (i, elem) in arr.iter_mut().enumerate() {
        elem.write(format!("item {}", i));
    }

    // On nightly: arr.map(|e| unsafe { e.assume_init() })
    unsafe { MaybeUninit::array_assume_init(arr) }
}

// DO: Use zeroed only for types where it's valid
fn good_zeroed() -> [u8; 1024] {
    // SAFETY: All-zero bytes is valid for u8
    unsafe { std::mem::zeroed() }
}

// DO: Initialize buffer properly
fn good_vec() -> Vec<u8> {
    let mut v = Vec::with_capacity(1024);

    // Option 1: Resize with default value
    v.resize(1024, 0);

    // Option 2: Use spare_capacity_mut
    let spare = v.spare_capacity_mut();
    for elem in spare.iter_mut().take(1024) {
        elem.write(0);
    }
    unsafe { v.set_len(1024); }

    v
}

// DO: Use MaybeUninit::uninit_array (nightly) or const array
fn good_uninit_array<const N: usize>() -> [MaybeUninit<u8>; N] {
    // Stable: create array of uninit
    [const { MaybeUninit::uninit() }; N]
}

MaybeUninit API

use std::mem::MaybeUninit;

// Creation
let uninit: MaybeUninit<T> = MaybeUninit::uninit();
let zeroed: MaybeUninit<T> = MaybeUninit::zeroed();
let init: MaybeUninit<T> = MaybeUninit::new(value);

// Writing
uninit.write(value);  // Returns &mut T

// Reading (unsafe)
let value: T = unsafe { uninit.assume_init() };
let ref_: &T = unsafe { uninit.assume_init_ref() };
let mut_: &mut T = unsafe { uninit.assume_init_mut() };

// Pointer access
let ptr: *const T = uninit.as_ptr();
let mut_ptr: *mut T = uninit.as_mut_ptr();

Checklist

  • Am I using mem::uninitialized()? → Replace with MaybeUninit
  • Am I using mem::zeroed() for non-POD types? → Use MaybeUninit
  • Am I setting Vec length without initialization? → Use proper initialization
  • Have I initialized all MaybeUninit before assume_init?
  • safety-03: Don't expose uninitialized memory in APIs
  • safety-01: Panic safety with partial initialization