Rust Undifined Behavior

小心UB,使用unsafe的时候多看接口文档

1. Memory

1.1 uninitialized memory

#![allow(invalid_value)]
use std::mem::{self, MaybeUninit};

let x: &i32 = unsafe { mem::zeroed() }; // undefined behavior! 
// The equivalent code with `MaybeUninit<&i32>`:
let x: &i32 = unsafe { MaybeUninit::zeroed().assume_init() }; // undefined behavior! 

上面即使不使用这个引用,也是UB

类似的,下面也是UB

let x: i32 = unsafe { mem::uninitialized() }; // undefined behavior!
let b: bool = unsafe { mem::uninitialized() };

关于这个UB,有讨论 Uninitialized memory varying from read to read

1.2 unchecked index

调用slice方法get_unchecked(),需要调用者保证不越界,如果越界则是UB,即使不读返回值。

1.3 alloc

全局alloc 不能申请size为0的内存 (所以Box不会为ZST真的申请内存)。 但rust中用指针读写ZST是可以的,但要求指针不为0且对齐。

1.4 misaligned

读写未对齐的指针是UB

1.5 offset

pub const unsafe fn offset(self, count: isize) -> *const T

Rust的offset比较严格,违反以下3点任何一个就是UB,

  • Both the starting and resulting pointer must be either in bounds or one byte past the end of the same allocated object.
  • The computed offset, in bytes, cannot overflow an isize.
  • The offset being in bounds cannot rely on “wrapping around” the address space. That is, the infinite-precision sum, in bytes must fit in a usize.

所以并不是简单算一个新地址就好,这些限制的好处是能让编译器做更激进的优化。

2. Error Handling

2.1 unwind

无论是从别的语言unwind进rust,还是rust unwind进别的语言,都是UB,所以要在FFI时注意异常的边界,要抓住panic可以用catch_unwind() 方法。

3. concurrency

3.1 data race

data race在Rust中算作UB,所以要合理控制并发