#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`:只能读取指针指向的内容