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 {
// 解引用裸指针需要 unsafe
let 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];
// 直接调用,无需 unsafe
safe_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() {
// 原子操作 - 线程安全且无需 unsafe
let 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 {
// 访问联合体字段需要 unsafe
println!("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),
}
}
更多推荐



所有评论(0)