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 函数或解引用原始指针。

默认情况下,不安全函数的主体也被认为是不安全代码块;这可以通过启用 unsafe_op_in_unsafe_fn lint 来更改。

通过将操作放入不安全代码块中,程序员声明他们已经注意满足该代码块内所有操作的额外安全条件。

不安全代码块是不安全函数的逻辑对偶:不安全函数定义了调用者必须履行的证明义务,而不安全代码块声明了代码块内调用的函数或操作的所有相关证明义务都已履行。有很多方法可以履行证明义务;例如,可以有运行时检查或数据结构不变性来保证某些属性肯定是正确的,或者不安全代码块可能在 unsafe fn 内部,在这种情况下,代码块可以使用该函数的证明义务来履行代码块内部产生的证明义务。

不安全代码块用于包装外部库、直接使用硬件或实现语言中未直接提供的功能。例如,Rust 提供了在语言中实现内存安全并发所需的语言特性,但标准库中线程和消息传递的实现使用了不安全代码块。

Rust 的类型系统是对动态安全要求的保守近似,因此在某些情况下,使用安全代码会产生性能成本。例如,双向链表不是树结构,只能在安全代码中使用引用计数指针表示。通过使用 unsafe 代码块将反向链接表示为原始指针,可以在不使用引用计数的情况下实现。(有关此特定示例的更深入探讨,请参阅 “Learn Rust With Entirely Too Many Linked Lists”。)

不安全 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 版本之前,允许不使用 unsafe 限定符的 extern 代码块。

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

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