unsafe 关键字

unsafe 关键字可以出现在多种不同的上下文中:不安全函数(unsafe fn)、不安全块(unsafe {})、不安全 trait(unsafe trait)、不安全 trait 实现(unsafe impl)、不安全外部块(unsafe extern)和不安全属性(#[unsafe(attr)])。根据其使用位置以及是否启用了 unsafe_op_in_unsafe_fn lint,它扮演着几种不同的角色:

  • 用于标记**定义**额外安全条件的的代码(unsafe fnunsafe trait
  • 用于标记需要**满足**额外安全条件的代码(unsafe {}unsafe impl、未启用 unsafe_op_in_unsafe_fnunsafe fnunsafe extern#[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 版本之前,允许使用未标记为 unsafeextern 块。

不安全属性(#[unsafe(attr)]

不安全属性是使用时必须遵守额外安全条件的属性。编译器无法检查这些条件是否已遵守。为了断言它们已遵守,这些属性必须包裹在 unsafe(..) 中,例如 #[unsafe(no_mangle)]