Keyword unsafe[−][src]
Expand description
memory safety 不能由类型系统验证的代码或接口。
unsafe
关键字有两个用途: 声明编译器无法检查的契约的存在 (unsafe fn
和 unsafe trait
),以及声明程序员检查了这些契约是否得到遵守 (unsafe {}
和 unsafe impl
,还有 unsafe fn
- 见下文)。
它们并不互斥,如 unsafe fn
所示。
不安全能力
无论如何,安全 Rust 都不会导致未定义行为。这称为 soundness: 类型正确的程序实际上具有所需的属性。Nomicon 对此主题有更详细的说明。
为了确保安全,Safe Rust 受到足够的限制,可以自动检查。但是,有时出于某些原因,需要编写正确的代码,而这些原因对于编译器来说太聪明了。在这种情况下,您需要使用不安全的 Rust。
除安全 Rust 之外,以下还有不安全 Rust 所具有的功能:
- 解引用 raw pointers
- 实现
unsafe
trait
- 调用
unsafe
函数 - 可变的
static
(包括extern
al 个) union
的访问字段
但是,这种额外的权力还伴随着额外的责任: 现在要确保声音的完整性。unsafe
关键字有助于明确标记需要担心的代码段。
unsafe
的不同含义
并非 unsafe
的所有用法都是等效的: 这里有些标记是程序员必须检查的契约的存在,而另一些是 “I have checked the contract, go ahead and do this”。
以下 discussion on Rust Internals 对此有更深入的说明,但这是要点的总结:
unsafe fn
: 调用此函数意味着遵守编译器无法强制执行的契约。unsafe trait
: 实现trait
意味着遵守编译器无法执行的契约。unsafe {}
: 程序员已检查了在块中进行操作所必需的契约,并保证该契约得到遵守。unsafe impl
: 程序员已经检查了实现 trait 所需的契约,并保证该契约得到遵守。
unsafe fn
在函数内的代码周围,它也像一个 unsafe {}
块。这意味着不仅要向调用者发出信号,而且还应 promises 维护函数内部操作的前提条件。
混合使用这两种含义可能会造成混淆,并且存在在进行 unsafe
操作时在此类函数中使用 unsafe {}
块的建议。
有关更多信息,请参见 Rustnomicon 和 Reference。
Examples
标记元素为 unsafe
unsafe
可以在函数上使用。请注意,在 extern
块中声明的函数和静态变量被隐式标记为 unsafe
(但未被声明为 extern "something" fn ...
的函数)。
无论在何处声明,可变静态变量始终是不安全的。方法也可以声明为 unsafe
:
static mut FOO: &str = "hello"; unsafe fn unsafe_fn() {} extern "C" { fn unsafe_extern_fn(); static BAR: *mut u32; } trait SafeTraitWithUnsafeMethod { unsafe fn unsafe_method(&self); } struct S; impl S { unsafe fn unsafe_method_on_struct() {} }Run
Traits 也可以声明为 unsafe
:
unsafe trait UnsafeTrait {}Run
由于 unsafe fn
和 unsafe trait
表示存在编译器无法强制执行的安全保证,因此对其进行记录很重要。标准库提供了许多示例,例如以下示例,它是 Vec::set_len
的摘录。
# Safety
部分说明了安全调用函数必须履行的契约。
/// 将 vector 的长度强制为 `new_len`。 /// 这是一个维操作,不保留该类型的任何普通不变式。 /// 通常,使用安全操作之一 (例如 `truncate`,`resize`,`extend` 或 `clear`) 来更改 vector 的长度。 /// # Safety /// - `new_len` 必须小于或等于 `capacity()`。 /// - `old_len..new_len` 上的元素必须初始化。 pub unsafe fn set_len(&mut self, new_len: usize)Run
使用 unsafe {}
块和 impl
s
执行 unsafe
操作需要一个 unsafe {}
块:
/// 解引用给定的指针。 /// # Safety /// `ptr` 必须对齐并且不能悬空。 unsafe fn deref_unchecked(ptr: *const i32) -> i32 { *ptr } let a = 3; let b = &a as *const _; // SAFETY: `a` 尚未丢弃,并且引用始终对齐,因此 `b` 是有效地址。 unsafe { assert_eq!(*b, deref_unchecked(b)); };Run
标记为 unsafe
的 Traits 必须使用 unsafe impl
进行复制。这为实现满足 trait 的安全保证的其他 unsafe
代码提供了保证。Send 和 Sync traits 是标准库中此行为的示例。
/// trait 的实现者必须保证元素始终可以通过索引 3 访问。 unsafe trait ThreeIndexable<T> { /// 返回 `&self` 中索引为 3 的元素的引用。 fn three(&self) -> &T; } // `[T; 4]` 的 `ThreeIndexable` 实现是 `unsafe`,因为实现者必须遵守编译器无法检查的契约,但作为一名程序员,我们知道在索引 3 处总会有一个有效元素要访问。 unsafe impl<T> ThreeIndexable<T> for [T; 4] { fn three(&self) -> &T { // SAFETY: 实现 trait 意味着总是有一个索引为 3 的元素可以访问。 unsafe { self.get_unchecked(3) } } } let a = [1, 2, 4, 8]; assert_eq!(a.three(), &8);Run