Rust unsafe
概述
Rust 中的 unsafe 关键字 用于标记哪些编译器无法完全验证安全性的代码。它允许你执行一些通常被 Rust 的安全规则禁止的操作,但同时要求程序员自己确保这些操作的安全性
什么是 unsafe ?
unsafe 不意味着代码一定危险,而是意味着:
- 编译器无法自动验证这段代码的内存安全性
- 程序员需要手动确保代码符合Rust 的安全约定
- 允许访问一些通常被禁止的低级功能
例子
unsafe 允许的五大操作
- 解引用裸指针
- 调用不安全函数
- 访问和修改可变静态变量
- 实现不安全 trait
- 访问联合体字段
解引用裸指针
Unsafe: 直接通过裸指针读取或写入数据
fn unsafe_raw_pointer() {let x = 10;let y = 20;// 创建裸指针是安全的let raw_ptr1 = &x as *const i32;let raw_ptr2 = &y as *const i32;unsafe {// 解引用裸指针需要 unsafelet value1 = *raw_ptr1;let value2 = *raw_ptr2;println!("Unsafe: {} + {} = {}", value1, value2, value1 + value2);}
}
safe: 使用引用(&和&mut),由编译器检查生命周期和借用规则
fn safe_references() {let x = 10;let y = 20;// 使用引用,编译器会验证安全性let ref1 = &x;let ref2 = &y;// 直接解引用,编译器保证安全let value1 = *ref1;let value2 = *ref2;println!("Safe: {} + {} = {}", value1, value2, value1 + value2);// 或者更简洁的方式println!("Safe simpler: {} + {} = {}", x, y, x + y);
}
调用不安全函数
unsafe: 调用被标记为unsafe的函数
// 声明不安全函数
unsafe fn dangerous_function(buffer: *mut [u8; 1024]) {if !buffer.is_null() {// 直接操作内存(*buffer)[0] = 42;}
}fn call_unsafe() {let mut data = [0u8; 1024];unsafe {dangerous_function(&mut data as *mut [u8; 1024]);}println!("Unsafe result: {}", data[0]);
}
safe: 调用safe函数,或者通过安全封装后的函数(内部可能包含unsafe,但对外是safe的接口)
// 安全函数声明
fn safe_function(buffer: &mut [u8; 1024]) {// 使用安全的索引访问buffer[0] = 42;
}fn call_safe() {let mut data = [0u8; 1024];// 直接调用,无需 unsafesafe_function(&mut data);println!("Safe result: {}", data[0]);
}
访问和修改可变静态变量
unsafe: 直接读写可变静态变量(static mut)
static mut GLOBAL_COUNTER: u32 = 0;fn unsafe_global_modification() {unsafe {GLOBAL_COUNTER += 1;println!("Unsafe global: {}", GLOBAL_COUNTER);}
}
safe: 使用原子类型或者通过Mutex等同步机制来安全地共享可变状态
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Mutex;// 使用原子操作
static SAFE_COUNTER: AtomicU32 = AtomicU32::new(0);// 或者使用互斥锁
static SAFE_DATA: Mutex<Vec<String>> = Mutex::new(Vec::new());fn safe_global_modification() {// 原子操作 - 线程安全且无需 unsafelet count = SAFE_COUNTER.fetch_add(1, Ordering::SeqCst);println!("Safe atomic: {}", count + 1);// 互斥锁保护 - 线程安全{let mut data = SAFE_DATA.lock().unwrap();data.push("hello".to_string());}
}
实现不安全trait
unsafe: 实现一个被标记为unsafe的trait
// 不安全 trait,实现者必须保证某些不变量
unsafe trait UnsafeSend: Send {// 实现者必须保证这个类型可以安全地跨线程发送
}unsafe impl UnsafeSend for i32 {// 我们知道 i32 可以安全发送
}struct MyUnsafeType {data: *mut u8,
}unsafe impl Send for MyUnsafeType {// 手动保证这个类型可以安全发送
}
safe: 实现普通的trait。
// 安全 trait,编译器会自动验证安全性
trait SafeTrait {fn do_something(&self);
}// 自动推导的 trait 实现
#[derive(Clone, Debug)]
struct MySafeType {data: Vec<u8>,
}impl SafeTrait for MySafeType {fn do_something(&self) {println!("Safe trait implementation");}
}// 自动实现 Send 和 Sync(因为所有字段都实现了这些 trait)
// 编译器自动验证,无需手动保证
访问联合体字段
unsafe: 访问Rust联合体(union)的字段
#[repr(C)]
union MyUnion {integer: u32,float: f32,bytes: [u8; 4],
}fn unsafe_union_access() {let u = MyUnion { integer: 0x40490FDB }; // PI 的 IEEE 754 表示unsafe {// 访问联合体字段需要 unsafeprintln!("Unsafe union - as integer: {}", u.integer);println!("Unsafe union - as float: {}", u.float);println!("Unsafe union - as bytes: {:?}", u.bytes);}
}
safe: 使用枚举(enum)或者结构体(struct)来避免直接访问联合体
// 使用枚举替代联合体
enum SafeVariant {Integer(u32),Float(f32),Bytes([u8; 4]),
}impl SafeVariant {fn as_integer(&self) -> Option<u32> {match self {SafeVariant::Integer(i) => Some(*i),_ => None,}}fn as_float(&self) -> Option<f32> {match self {SafeVariant::Float(f) => Some(*f),_ => None,}}fn as_bytes(&self) -> Option<[u8; 4]> {match self {SafeVariant::Bytes(b) => Some(*b),_ => None,}}
}fn safe_enum_access() {let variant = SafeVariant::Integer(0x40490FDB);// 安全访问,类型安全if let Some(int_val) = variant.as_integer() {println!("Safe enum - as integer: {}", int_val);}// 或者使用模式匹配match variant {SafeVariant::Integer(i) => println!("Matched integer: {}", i),SafeVariant::Float(f) => println!("Matched float: {}", f),SafeVariant::Bytes(b) => println!("Matched bytes: {:?}", b),}
}