Appearance
19.1 unsafe 关键字
Rust 的 unsafe 关键字有以下几种用法:
用于修饰函数 fn;
用于修饰代码块;
用于修饰 trait;
用于修饰 impl。
当一个 fn 是 unsafe 的时候,意味着我们在调用这个函数的时候需要非常小心。它可能要求调用者满足一些其他的重要约束,而这些约束条件无法由编译器自动检查来保证。有 unsafe 修饰的函数,要么使用 unsafe 语句块调用,要么在 unsafe 函数中调用。因此需要注意,unsafe 函数是具有“传递性”的,unsafe 函数的“调用者”也必须用 unsafe 修饰。
比如,String::from_raw_parts 就是一个 unsafe 函数,它的签名如下:
rust
pub unsafe fn from_raw_parts(buf: *mut u8, length: usize, capacity: usize) -> String
它之所以是 unsafe 的,是因为 String 类型对所有者有一个保证:它内部存储的是合法的 utf-8 字符串。而这个函数没有检查传递进来的这个缓冲区是否满足这个条件,所以使用者必须这样调用:
rust
// 自己保证这个缓冲区包含的是合法的 utf-8 字符串
let s = unsafe { String::from_raw_parts(ptr as *mut _, len, capacity) } ;
上面这个写法就是 unsafe 代码块的用法。使用 unsafe 关键字包围起来的语句块,里面可以做一些一般情况下做不了的事情。但是,它也是有规矩的。与普通代码比起来,它多了以下几项能力:
对裸指针执行解引用操作;
读写可变静态变量;
读 union 或者写 union 的非 Copy 成员;
调用 unsafe 函数。
在 Rust 中,有些地方必须使用 unsafe 才能实现。比如标准库提供的一系列 intrinsic 函数,很多都是 unsafe 的,再比如调用 extern 函数必须在 unsafe 中实现。另外,一些重要的数据结构内部也使用了 unsafe 来实现一些功能。
当 unsafe 修饰一个 trait 的时候,那么意味着实现这个 trait 也需要使用 unsafe。比如在后面讲线程安全的时候会着重讲解的 Send、Sync 这两个 trait。因为它们很重要,是实现线程安全的根基,如果由程序员来告诉编译器,强制指定一个类型是否满足 Send、Sync,那么程序员自己必须很谨慎,必须很清楚地理解这两个 trait 代表的含义,编译器是没有能力推理验证这个 impl 是否正确的。这种 impl 对程序的安全性影响很大。