#rust - 来源:《Programming Rust 2nd Edition》 Chapter 22 - 通过将一个 block 或 函数 标记为 unsafe,就可以在标准库中调用 `unsafe` 函数,dereference unsafe 指针,调用其他语言如 C/C++ 编写的函数等。 ---------- # 1. Unsafe From What - unsafe 代码 Rust 通常无法进行约束(类型检查、lifetime 检查等),只是在文档层面进行阐述。由用户来保证代码是安全的。 ```Rust fn main() { let mut a: usize = 0; let ptr = &mut a as *mut usize; unsafe { *ptr.offset(3) = 0x7ffff72f484c; } } ``` - 这段代码覆盖了 main 函数的返回地址,引发未定义行为,导致不可预知的结果(错误、崩溃等)。 --------------- # 2. Unsafe Blocks ```Rust unsafe { String::from_utf8_unchecked(ascii) } ``` - 通过一个 unsafe block 就可以调用 unsafe 的函数。 - 一个 unsafe block 为你解锁了 5 个额外的选项: - 可以调用 unsafe 函数 - 可以 dereference raw pointer - safe code 可以传递 raw pointer、比较它们、创建它们(通过从引用转换过来,甚至可以通过整数转换过来) - 但是真正地使用它们访问内存,则只能通过 unsafe - 可以访问 union 的 fields。 > The compiler can’t be sure contain valid bit patterns for their respective types. - 可以访问 mutable static variables。 - 可以访问通过 Rust foreign function interface 声明的函数和变量。 - 它们即使是 immutable 的也被认定为 unsafe,因为它们是由其他语言编写的,不一定会遵守 Rust 的安全规则。 ------ # 3. Unsafe Functions - unsafe 函数的 **body** 将会自动地被考虑为 unsafe block。 - 只能在一个 unsafe block 内调用 unsafe 函数。 ---- # 4. Unsafe Block or Unsafe Function - 何时使用 unsafe block, 或者将整个函数标记为 unsafe - 如果可能误用函数,编译正常但引发未定义行为,则必须将函数标记为 unsafe - 否则,该函数就是 safe 的,不应该标记为 unsafe。 - 不要仅仅因为在函数体使用了 unsafe 特性,就将函数标记为 unsafe。 - 这将会使函数使用起来困难 ---- # 5. Undefined Behavior - 编译器是一个 translator,将一种语言翻译成另外一种,Rust compiler 将 Rust 语言翻译成机器语言。 - 如何判断两种语言完全相等呢: - 我们通常说,如果两个程序在执行时总是具有相同的可见行为,那么它们就是等价的。 - 例如执行相同的系统调用、以等价的方式与外部库交互等等 - 如下代码: ```rust let i = 10; // shared reference,不改变 i 的值 very_trustworthy(&i); // 因此,可以确信输出总是 1000 println!("{}", i * 100); ``` - Rust 可以将其翻译为:(两者的可见行为是一样的) ```rust very_trustworthy(&10); println!("{}", 1000); ``` - 下面的代码打破了 shared references 的规则,导致上面翻译前后的程序执行结果不一样。 ```rust fn very_trustworthy(shared: &i32) { unsafe { // Turn the shared reference into a mutable pointer. // This is undefined behavior. let mutable = shared as *const i32 as *mut i32; *mutable = 20; } } ``` - Rust 编译器需要依赖一些假设的成立,来编译程序。 - Rust rules for well-behaved programs: - **不能读取未/初始化的内存** - **不能创建非法的 primitive values**: - 空的 references、bloxes,fn 指针 - bool 值是 0 或 1 之外的值 - 非法的 enum 值 - 无效的 char 值(不符合 unicode 标准) - str values 不是 well-formed UTF-8 - Fat pointers 的 vtables/slice length 非法 - Any value of the type ! - `pub fn unreachable(x: Void) -> ! {} - **遵守关于引用的相关规则**: - 引用声明期不能超过它指向的对象 - shared access 必须是只读的,mutable access 必须是排他的。 - **不能对 null 进行 dereference, incorrectly aligned, dangling pointers** - **不能使用指针访问与其关联的分配之外的内存。** - **没有 data races** - The program must not unwind across a call made from another language - **必须遵守标准库函数的约束** --- # 6. Unsafe Traits > An unsafe trait is a trait that has a contract Rust cannot check or enforce that implementers must satisfy to avoid undefined behavior. - Unsafe trait是具有 Rust 无法检查或强制的契约的 trait,实现者必须满足该契约才能避免未定义的行为。 - 为了实现一个 unsafe trait,你必须将 implementation 标记为 unsafe。 - 由你来理解 trait 的 contract,并且保证你所实现的类型满足它。实现错误就可能导致未定义的行为。 - std::marker::Send 和 std::marker::Sync 是典型的 unsafe traits。 ---- # 7. Raw Pointers - Raw pointer 在 Rust 中是一类不受约束的指针。 - Dereference raw pointers 只能在 unsafe block 中进行。 - Raw pointers 本质上等价于 C 和 C++ 的指针,所以在跟这些语言交互时很有用。 - Raw pointer 有两种类型(且只能是两种类型之一): - `*mut T` :可以修改指针指向的内容 - `*const T`:只能读取指针指向的内容