unsafe 关键字
unsafe 关键字用于创建或解除证明某事安全的义务。具体来说:
- 它用于标记定义必须在其他地方维护的额外安全条件的代码。
- 这包括
unsafe fn、unsafe static和unsafe trait。
- 这包括
- 它用于标记程序员断言满足在其他地方定义的安全条件的代码。
- 这包括
unsafe {}、unsafe impl、没有unsafe_op_in_unsafe_fn的unsafe fn、unsafe extern和#[unsafe(attr)]。
- 这包括
以下讨论了每种情况。有关一些说明性示例,请参阅关键字文档。
unsafe 关键字可以出现在几种不同的上下文中:
- 不安全函数(
unsafe fn) - 不安全块(
unsafe {}) - 不安全 trait(
unsafe trait) - 不安全 trait 实现(
unsafe impl) - 不安全外部块(
unsafe extern) - 不安全外部静态(
unsafe static) - 不安全属性(
#[unsafe(attr)])
不安全函数(unsafe fn)
不安全函数是在所有上下文中和/或对所有可能输入不安全的函数。我们说它们具有额外安全条件,这是所有调用者必须维护的要求,编译器不会检查。例如,get_unchecked 具有额外安全条件,即索引必须在边界内。不安全函数应附带解释这些额外安全条件是什么的文档。
此类函数必须以关键字 unsafe 为前缀,并且只能从 unsafe 块内部调用,或在没有 unsafe_op_in_unsafe_fn lint 的 unsafe fn 内部调用。
不安全块(unsafe {})
代码块可以用 unsafe 关键字前缀,以允许使用不安全章节中定义的不安全操作,例如调用其他不安全函数或解引用裸指针。
默认情况下,不安全函数的主体也被视为不安全块;这可以通过启用 unsafe_op_in_unsafe_fn lint 来更改。
通过将操作放入不安全块,程序员声明他们已注意满足该块内所有操作的额外安全条件。
不安全块是不安全函数的逻辑对偶:不安全函数定义了调用者必须维护的证明义务,不安全块声明块内调用的函数或操作的所有相关证明义务已被解除。有许多方式可以解除证明义务;例如,可能有运行时检查或数据结构不变量保证某些属性肯定为真,或者不安全块可能在 unsafe fn 内,在这种情况下,该块可以使用该函数的证明义务来解除块内产生的证明义务。
不安全块用于包装外部库、直接使用硬件或实现语言中不存在的功能。例如,Rust 提供了在语言中实现内存安全并发所需的语言功能,但标准库中线程和消息传递的实现使用了不安全块。
Rust 的类型系统是动态安全需求的保守近似,因此在某些情况下使用安全代码会有性能成本。例如,双向链表不是树结构,在安全代码中只能用引用计数指针表示。通过使用 unsafe 块将反向链接表示为裸指针,可以在没有引用计数的情况下实现。(有关此特定示例的更深入探讨,请参阅“通过太多链表学习 Rust”。)
不安全 trait(unsafe trait)
不安全 trait 是具有额外安全条件的 trait,必须由 trait 的实现维护。不安全 trait 应附带解释这些额外安全条件是什么的文档。
此类 trait 必须以关键字 unsafe 为前缀,并且只能由 unsafe impl 块实现。
不安全 trait 实现(unsafe impl)
实现不安全 trait 时,实现需要以 unsafe 关键字为前缀。通过编写 unsafe impl,程序员声明他们已注意满足 trait 所需的额外安全条件。
不安全 trait 实现是不安全 trait 的逻辑对偶:不安全 trait 定义了实现必须维护的证明义务,不安全实现声明所有相关证明义务已被解除。
不安全外部块(unsafe extern)
声明外部块的程序员必须确保其中包含的项的签名是正确的。未能这样做可能导致未定义行为。通过编写 unsafe extern 来指示已满足此义务。
2024 Edition differences
在 2024 版本之前,允许
extern块不需要限定为unsafe。
不安全属性(#[unsafe(attr)])
不安全属性是使用属性时必须维护额外安全条件的属性。编译器无法检查这些条件是否已满足。为了断言它们已满足,这些属性必须包装在 unsafe(..) 中,例如 #[unsafe(no_mangle)]。