Rust从入门到实战系列二百二十一:不安全的超能力

可以通过 unsafe 关键字来切换到不安全 Rust,接着可以开启一个新的存放不安全代码的块。这里有五类
可以在不安全 Rust 中进行而不能用于安全 Rust 的操作,它们称之为 ” 不安全的超能力。” 这些超能力是:
• 解引用裸指针
• 调用不安全的函数或方法
• 访问或修改可变静态变量
• 实现不安全 trait
• 访问 union 的字段
有一点很重要,unsafe 并不会关闭借用检查器或禁用任何其他 Rust 安全检查:如果在不安全代码中使
用引用,它仍会被检查。unsafe 关键字只是提供了那五个不会被编译器检查内存安全的功能。你仍然能
在不安全块中获得某种程度的安全。
再者,unsafe 不意味着块中的代码就一定是危险的或者必然导致内存安全问题:其意图在于作为程序员
你将会确保 unsafe 块中的代码以有效的方式访问内存。
人是会犯错误的,错误总会发生,不过通过要求这五类操作必须位于标记为 unsafe 的块中,就能够知道
任何与内存安全相关的错误必定位于 unsafe 块内。保持 unsafe 块尽可能小,如此当之后调查内存 bug
时就会感谢你自己了。
为了尽可能隔离不安全代码,将不安全代码封装进一个安全的抽象并提供安全 API 是一个好主意,当我
们学习不安全函数和方法时会讨论到。标准库的一部分被实现为在被评审过的不安全代码之上的安全抽
象。这个技术防止了 unsafe 泄露到所有你或者用户希望使用由 unsafe 代码实现的功能的地方,因为使
用其安全抽象是安全的。
让我们按顺序依次介绍上述五个超能力,同时我们会看到一些提供不安全代码的安全接口的抽象。
解引用裸指针
回到第四章的 ” 悬垂引用” 部分,那里提到了编译器会确保引用总是有效的。不安全 Rust 有两个被称
为 裸指针(raw pointers)的类似于引用的新类型。和引用一样,裸指针是不可变或可变的,分别写作
*const T 和 *mut T。这里的星号不是解引用运算符;它是类型名称的一部分。在裸指针的上下文中,不
可变意味着指针解引用之后不能直接赋值。
裸指针与引用和智能指针的区别在于
• 允许忽略借用规则,可以同时拥有不可变和可变的指针,或多个指向相同位置的可变指针
• 不保证指向有效的内存
• 允许为空
• 不能实现任何自动清理功能
通过去掉 Rust 强加的保证,你可以放弃安全保证以换取性能或使用另一个语言或硬件接口的能力,此时
Rust 的保证并不适用。
示例 19-1 展示了如何从引用同时创建不可变和可变裸指针。

fn main() {

let mut num = 5;
let r1 = &num as *const i32;
let r2 = &mut num as *mut i32;

}

示例 19-1: 通过引用创建裸指针
注意这里没有引入 unsafe 关键字。可以在安全代码中 创建裸指针,只是不能在不安全块之外 解引用裸
指针,稍后便会看到。
这里使用 as 将不可变和可变引用强转为对应的裸指针类型。因为直接从保证安全的引用来创建他们,可
以知道这些特定的裸指针是有效,但是不能对任何裸指针做出如此假设。
接下来会创建一个不能确定其有效性的裸指针,示例 19-2 展示了如何创建一个指向任意内存地址的裸
指针。尝试使用任意内存是未定义行为:此地址可能有数据也可能没有,编译器可能会优化掉这个内存
访问,或者程序可能会出现段错误(segmentation fault)。通常没有好的理由编写这样的代码,不过却
是可行的:

fn main() {

let address = 0x012345usize;
let r = address as *const i32;

}

示例 19-2: 创建指向任意内存地址的裸指针
记得我们说过可以在安全代码中创建裸指针,不过不能 解引用裸指针和读取其指向的数据。现在我们要
做的就是对裸指针使用解引用运算符 *,这需要一个 unsafe 块,如示例 19-3 所示:

fn main() {

let mut num = 5;
let r1 = &num as *const i32;
let r2 = &mut num as *mut i32;
unsafe {
println!(“r1 is: {}”, *r1);
println!(“r2 is: {}”, *r2);
}

}

示例 19-3: 在 unsafe 块中解引用裸指针
创建一个指针不会造成任何危险;只有当访问其指向的值时才有可能遇到无效的值。
还需注意示例 19-1 和 19-3 中创建了同时指向相同内存位置 num 的裸指针 *const i32 和 *mut i32。相
反如果尝试同时创建 num 的不可变和可变引用,将无法通过编译,因为 Rust 的所有权规则不允许在拥
有任何不可变引用的同时再创建一个可变引用。通过裸指针,就能够同时创建同一地址的可变指针和不
可变指针,若通过可变指针修改数据,则可能潜在造成数据竞争。请多加小心!
既然存在这么多的危险,为何还要使用裸指针呢?一个主要的应用场景便是调用 C 代码接口,这在下一
部分 ” 调用不安全函数或方法” 中会讲到。另一个场景是构建借用检查器无法理解的安全抽象。让我们先
介绍不安全函数,接着看一看使用不安全代码的安全抽象的例子。

  • 9
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值