概述

Rust 中的 unsafe 关键字 用于标记哪些编译器无法完全验证安全性的代码。它允许你执行一些通常被 Rust 的安全规则禁止的操作,但同时要求程序员自己确保这些操作的安全性

什么是 unsafe ?

unsafe 不意味着代码一定危险,而是意味着:

  1. 编译器无法自动验证这段代码的内存安全性
  2. 程序员需要手动确保代码符合Rust 的安全约定
  3. 允许访问一些通常被禁止的低级功能

例子

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),
    }
}
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐