unsafe-Rust
unsafe-Rust
xiu用于跟硬件沟通的编程逻辑无法通过编译器的检查,为了实现相应的程序功能,unsafe代码块会解除检查限制,允许我们编写不安全
的代码实现自己的需求。需要注意的是unsafe模式可以绕过编译阶段的检查,但是执行的时候如果逻辑有错误仍然会panic
- 解引用裸指针,就如上例所示
- 调用一个
unsafe
或外部的函数 - 访问或修改一个可变的静态变量
- 实现一个
unsafe
特征 - 访问
union
中的字段
解引用裸指针
裸指针(raw pointer,又称原生指针) 在功能上跟引用类似,同时它也需要显式地注明可变性。裸指针长这样: *const T
和 *mut T
裸指针:
- 可以绕过 Rust 的借用规则,可以同时拥有一个数据的可变、不可变指针,甚至还能拥有多个可变的指针
- 并不能保证指向合法的内存
- 可以是
null
- 没有实现任何自动的回收 (drop)
裸指针跟 C 指针是非常像的,使用它需要以牺牲安全性为前提,但我们获得了更好的性能,也可以跟其它语言或硬件打交道。
基于引用创建裸指针
1 | fn main() { |
基于内存地址创建裸指针
1 | fn main() { |
基于智能指针创建裸指针
1 | let a: Box<i32> = Box::new(10); |
调用unsafe函数或方法
unsafe
函数从外表上来看跟普通函数并无区别,唯一的区别就是它需要使用 unsafe fn
来进行定义。这种定义方式是为了告诉调用者:当调用此函数时,你需要注意它的相关需求,因为 Rust 无法担保调用者在使用该函数时能满足它所需的一切需求
1 | unsafe fn dangerous() {} |
用安全抽象包裹 unsafe 代码
一个函数包含了 unsafe
代码不代表我们需要将整个函数都定义为 unsafe fn
我们编写一个函数,需要将一个数组分成两个切片,且每一个切片都要求是可变的。对同一个数组做两个可变借用,违反Rust借用规则
1 | fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) { |
我们知道这两个引用肯定不会冲突,但是rust编译器无法判断是否安全就禁止编译,这种情况下可以借助unsafe
1 | use std::slice; |
**split_at_mut 使用了 unsafe
,但我们无需将其声明为 unsafe fn
**,这种情况下就是使用安全的抽象包裹 unsafe
代码,这里的 unsafe
使用是非常安全的,因为我们从合法数据中创建了的合法指针
FFI
FFI
(Foreign Function Interface)可以用来与其它语言进行交互
unsafe
的另一个重要目的就是对 FFI
提供支持
通过 FFI
, 我们的 Rust 代码可以跟其它语言的外部代码进行交互
1 | extern "C" { |
访问或修改一个可变的静态变量
实现unsafe特性
通过 unsafe impl
的使用,我们告诉编译器:相应的正确性由我们自己来保证
1 | unsafe trait Foo { |
访问 union 中的字段
union
,它主要用于跟 C
代码进行交互,所有字段共享存储空间有数据覆盖的风险
1 | #[repr(C)] |