通过使用 unsafe 关键字,可以将 Rust 安全代码和非安全代码建立边界。一般来说 unsafe 代码(fun/block/trait)都有使用前提,使用方需要保证满足这些约束,防止出现未定义的行为。
尽量使用安全代码,这样 Rust 编译器、type checker、borrow checker 和其它静态 checker 会详细检查代码的合法性。unsafe 代码不会有这些安全性优势,需要开发者自己来保证。常见的做法是:将 unsafe block 封装在安全的函数中。
unsafe {} 用于指示编译器忽略一些严格的安全检查(但不忽略所有检查), 主要使用场景:
- 读写裸指针(安全代码可以创建、转移、比较 raw pointer,但是不能解引用和赋值 raw pointer);
- 调用外部库函数(FFI 函数);
- 调用 unsafe 函数或方法;
- 读写 static mut 全局变量;
- 实现 unsafe trait;
// from_utf8_unchecked 是 unsaft 函数,只能在 unsafe 上下文中调用
unsafe {
String::from_utf8_unchecked(ascii)
}
// 调用外部函数接口
extern "C" {
fn abs(input: i32) -> i32;
}
fn main() {
unsafe {
println!("Absolute value of -3 according to C: {}", abs(-3));
}
}
// 读写 static mut 变量
static mut COUNTER: u32 = 0;
fn add_to_count(inc: u32) {
unsafe {
COUNTER += inc;
}
}
fn main() {
add_to_count(3);
unsafe {
println!("COUNTER: {}", COUNTER);
}
}
在 unsafe block 中读写裸指针:
fn main() {
let mut a: usize = 0;
let ptr = &mut a as *mut usize;
unsafe {
*ptr.offset(3) = 0x7ffff72f484c;
}
}
// $ cargo build
// Compiling unsafe-samples v0.1.0
// Finished debug [unoptimized + debuginfo] target(s) in 0.44s
// $ ../../target/debug/crash
// crash: Error: .netrc file is readable by others.
// crash: Remove password or make file unreadable by others. Segmentation fault (core dumped)
unsafe function 是添加了 unsafe 标识的函数,它的 body 对应是 unsafe block。
- unsafe 和普通函数完全一样,只是用 unsafe 标记后,表示调用该函数需要满足一些先决条件,需要开发者来保证。
- 只能在 unsafe function/block 中调用 unsafe 函数。
pub unsafe fn from_bytes_unchecked(bytes: Vec<u8>) -> Ascii {
Ascii(bytes)
}
// 使用 unsafe 特性实现的安全函数
use std::slice;
fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
let len = slice.len();
let ptr = slice.as_mut_ptr();
assert!(mid <= len);
unsafe { // 需要在 unsafe block 中调用 unsafe 函数
(slice::from_raw_parts_mut(ptr, mid),
slice::from_raw_parts_mut(ptr.offset(mid as isize), len - mid))
}
}
use std::slice;
let address = 0x01234usize;
let r = address as *mut i32;
let slice : &[i32] = unsafe {
slice::from_raw_parts_mut(r, 10000)
};
unsafe trait 是添加了 unsafe 标识的 trait,在 impl 它时,也必须添加 unsafe 标识:
- unsafe trait 和普通 trait 完全一样,只是表明该 trait 的定义或实现有一些前提约束,Rust 编译器无法检查,需要开发者来保证。
// 定义 unsafe trait
pub unsafe trait Zeroable {}
// 实现 unsafe trait
unsafe impl Zeroable for u8 {}
unsafe impl Zeroable for i32 {}
unsafe impl Zeroable for usize {}
// and so on for all the integer types
// 使用 unsafe trait,不一定需要 unsafe 函数
use core::nonzero::Zeroable;
fn zeroed_vector<T>(len: usize) -> Vec<T> where T: Zeroable
{
let mut vec = Vec::with_capacity(len);
unsafe {
std::ptr::write_bytes(vec.as_mut_ptr(), 0, len);
vec.set_len(len);
}
vec
}