G.UNS.MEM.01 使用 MaybeUninit<T> 来处理未初始化的内存

【级别】 要求

【描述】

Rust 编译器要求变量要根据其类型正确初始化。

比如引用类型的变量必须对齐且非空。这是一个必须始终坚持的不变量,即使在 Unsafe 代码中也是如此。因此,零初始化引用类型的变量会导致立即未定义行为,无论该引用是否访问过内存。

编译器利用这一点,进行各种优化,并且可以省略运行时检查。

使用前请仔细查看 MaybeUninit<T> 相关文档。

【反例】

由调用者来保证MaybeUninit<T>确实处于初始化状态。当内存尚未完全初始化时调用 assume_init() 会导致立即未定义的行为。


#![allow(unused)]

fn main() {
use std::mem::{self, MaybeUninit};
// 不符合:零初始化引用
let x: &i32 = unsafe { mem::zeroed() }; // undefined behavior! ⚠️
// 等价于 `MaybeUninit<&i32>`:
let x: &i32 = unsafe { MaybeUninit::zeroed().assume_init() }; // undefined behavior! 
// 不符合:布尔值必须初始化
let b: bool = unsafe { mem::uninitialized() }; // undefined behavior! ⚠️
// 等价于 `MaybeUninit<bool>`:
let b: bool = unsafe { MaybeUninit::uninit().assume_init() }; // undefined behavior! 
// 不符合:整数类型也必须初始化
let x: i32 = unsafe { mem::uninitialized() }; // undefined behavior! ⚠️
// 等价于 `MaybeUninit<i32>`:
let x: i32 = unsafe { MaybeUninit::uninit().assume_init() }; 

// 不符合:Vec未初始化内存使用 set_len 是未定义行为
let mut vec: Vec<u8> = Vec::with_capacity(1000);
unsafe { vec.set_len(1000); }
reader.read(&mut vec); // undefined behavior!
}

【正例】


#![allow(unused)]
fn main() {
use std::mem::MaybeUninit;

let mut x = MaybeUninit::<bool>::uninit();
x.write(true); // 符合:这里正确进行了初始化
let x_init = unsafe { x.assume_init() }; // 通过 assume_init 对 MaybeUninit 的内存取值
assert_eq!(x_init, true);

// 符合:下面数组应该是可以的
let _: [MaybeUninit<bool>; 5] = unsafe {
    MaybeUninit::uninit().assume_init()
};

// 符合:Vec 未初始化内存正确处理
let mut vec: Vec<u8> = vec![0; 1000];
reader.read(&mut vec);
// 符合
let mut vec: Vec<MaybeUninit<T>> = Vec::with_capacity(1000);
vec.set_len(1000);  // `MaybeUninit` can be uninitialized
// 符合:
let mut vec: Vec<u8> = Vec::with_capacity(1000);
let remaining = vec.spare_capacity_mut();  // `&mut [MaybeUninit<u8>]`
// perform initialization with `remaining`
vec.set_len(...);  // Safe to call `set_len()` on initialized part
}

【例外】

在能保证 MaybeUninit 不需要初始化的情况下使用 assume_init 是安全的。


#![allow(unused)]
fn main() {
pub unsafe trait Array: Sized {
  
    /// Same array but item is wrapped with
    /// [`MaybeUninit<_>`](core::mem::MaybeUninit).
    /// ```
    /// # use arraylib::Array; fn dummy<T>() where
    /// [T; 4]: Array<Item = T, Maybe = [core::mem::MaybeUninit<T>; 4]>
    /// # {}
    /// ```
    type Maybe: Array<Item = MaybeUninit<Self::Item>>;

    /// [`MaybeUninit<T>`]: core::mem::MaybeUninit
    #[inline]
    // Initializing generic type with uninitialized state seems insane, but is
    // unsafe trait and `Array` guarantees that it's an array. And `Array::Maybe`
    // is an array of `MaybeUninit` that doesn't require initialization, so
    // everything is ok
    // 这里是一个数组,可以保证不需要去初始化
    #[allow(clippy::uninit_assumed_init)]
    fn uninit() -> Self::Maybe {
        unsafe {
            // ## Safety
            //
            // Completely safe as `MaybeUninit` don't require initialization
            MaybeUninit::uninit().assume_init()
        }
    }
}
}

【Lint 检测】

lint nameClippy 可检测Rustc 可检测Lint Grouplevel
uninit_assumed_inityesnocorrectnessdeny
uninit_vecyesnocorrectnessdeny