Keyword unsafe[−][src]
Expand description
memory safety 不能由类型系统验证的代码或接口。
unsafe
关键字有两个用途:声明编译器无法检查的契约的存在 (unsafe fn
和 unsafe trait
),以及声明程序员检查了这些契约是否得到遵守 (unsafe {}
和 unsafe impl
,还有 unsafe fn
- 见下文)。
它们并不互斥,如 unsafe fn
所示。
不安全能力
无论如何,安全 Rust 都不会导致未定义行为。这称为 可靠性: 类型正确的程序实际上具有所需的属性。Nomicon 对此主题有更详细的说明。
为了确保安全,Safe Rust 受到足够的限制,可以自动检查。但是,有时出于某些原因,需要编写正确的代码,而这些原因对于编译器来说太聪明了。在这种情况下,您需要使用不安全的 Rust。
除安全 Rust 之外,以下还有不安全 Rust 所具有的功能:
但是,这种额外的权力还伴随着额外的责任:现在要确保声音的完整性。unsafe
关键字有助于明确标记需要担心的代码段。
unsafe
的不同含义
并非 unsafe
的所有用法都是等效的:这里有些标记是程序员必须检查的契约的存在,而另一些是 “我已经检查了契约,继续做这个吧”。
以下 关于 Rust 内部的讨论 对此有更深入的说明,但这是要点的总结:
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() {}
}
RunTraits 也可以声明为 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