强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Rust 系统编程语言完全教程 / 第22章:Unsafe Rust

第22章:Unsafe Rust

22.1 为什么需要 Unsafe

Rust 的安全保证在某些场景下过于保守,unsafe 允许你绕过部分检查:

能力Safe RustUnsafe Rust
解引用裸指针
调用 unsafe 函数
访问可变静态变量
实现 unsafe trait
访问 union 字段

注意: unsafe 不会关闭借用检查器或其他安全检查,它只解锁上述五种能力。


22.2 裸指针(Raw Pointer)

创建与解引用

fn main() {
    let mut num = 42;

    // 创建裸指针(safe)
    let r1 = &num as *const i32;
    let r2 = &mut num as *mut i32;

    println!("r1 = {:?}, r2 = {:?}", r1, r2);

    // 解引用裸指针(unsafe)
    unsafe {
        println!("*r1 = {}", *r1);
        *r2 = 100;
        println!("*r2 = {}", *r2);
    }

    println!("num = {}", num); // 100
}

裸指针的特点

特性引用裸指针
空值❌ 不允许✅ 可以为空
保证有效❌ 不保证
自动释放
借用规则✅ 检查❌ 不检查
可变性需声明不需要

实用场景:拆分借用

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 {
        (
            std::slice::from_raw_parts_mut(ptr, mid),
            std::slice::from_raw_parts_mut(ptr.add(mid), len - mid),
        )
    }
}

fn main() {
    let mut data = vec![1, 2, 3, 4, 5, 6];
    let (left, right) = split_at_mut(&mut data, 3);
    println!("左: {:?}", left);   // [1, 2, 3]
    println!("右: {:?}", right);  // [4, 5, 6]
}

22.3 unsafe 函数与 trait

unsafe 函数

/// 从裸指针读取值
/// 调用者必须确保指针有效且已对齐
unsafe fn dangerous_read(ptr: *const i32) -> i32 {
    *ptr
}

fn main() {
    let num = 42;
    let ptr = &num as *const i32;

    let value = unsafe { dangerous_read(ptr) };
    println!("值: {}", value);
}

unsafe trait

/// 标记类型可以安全地跨线程共享
unsafe trait Send {}

/// 标记类型可以安全地从多线程引用
unsafe trait Sync {}

// 手动实现 unsafe trait
unsafe trait MyUnsafeTrait {
    fn dangerous_method(&self);
}

struct MyType;

unsafe impl MyUnsafeTrait for MyType {
    fn dangerous_method(&self) {
        println!("实现 unsafe trait 方法");
    }
}

fn main() {
    let obj = MyType;
    obj.dangerous_method();
}

22.4 FFI(外部函数接口)

调用 C 函数

// 声明外部 C 函数
extern "C" {
    fn abs(input: i32) -> i32;
    fn sqrt(input: f64) -> f64;
    fn strlen(s: *const std::ffi::c_char) -> usize;
}

fn main() {
    // 调用 C 标准库函数
    unsafe {
        println!("abs(-5) = {}", abs(-5));
        println!("sqrt(4.0) = {}", sqrt(4.0));
    }

    // 调用 strlen
    let c_str = std::ffi::CString::new("Hello, C!").unwrap();
    unsafe {
        let len = strlen(c_str.as_ptr());
        println!("strlen = {}", len);
    }
}

暴露 Rust 函数给 C

// 导出给 C 的函数
#[no_mangle]
pub extern "C" fn rust_add(a: i32, b: i32) -> i32 {
    a + b
}

#[no_mangle]
pub extern "C" fn rust_greet(name: *const std::ffi::c_char) {
    let name = unsafe {
        std::ffi::CStr::from_ptr(name).to_str().unwrap_or("unknown")
    };
    println!("Hello, {}!", name);
}

fn main() {
    // 在 Rust 中也可以调用
    println!("2 + 3 = {}", rust_add(2, 3));

    let name = std::ffi::CString::new("Rust").unwrap();
    rust_greet(name.as_ptr());
}

字符串转换

use std::ffi::{CString, CStr};
use std::os::raw::c_char;

fn main() {
    // Rust String → C 字符串
    let rust_string = "Hello, C!";
    let c_string = CString::new(rust_string).expect("CString::new failed");
    let c_ptr: *const c_char = c_string.as_ptr();

    // C 字符串 → Rust &str
    unsafe {
        let c_str = CStr::from_ptr(c_ptr);
        let rust_str = c_str.to_str().expect("Invalid UTF-8");
        println!("Rust: {}", rust_str);
    }

    // 注意:CString 拥有数据,CStr 只是借用
    // CStr::from_ptr 要求指针指向有效的以 null 结尾的字符串
}

22.5 Union 类型

#[repr(C)]
union IntOrFloat {
    i: i32,
    f: f32,
}

fn main() {
    let mut value = IntOrFloat { i: 42 };

    // 访问 union 字段需要 unsafe
    unsafe {
        println!("作为整数: {}", value.i);
    }

    value.f = 3.14;

    unsafe {
        println!("作为浮点: {}", value.f);
        // 读取之前的字段是未定义行为
        // println!("作为整数: {}", value.i); // 位模式不同
    }
}

22.6 实现安全抽象

Vec::push 的简化实现

use std::ptr;

struct MyVec<T> {
    ptr: *mut T,
    len: usize,
    cap: usize,
}

impl<T> MyVec<T> {
    fn new() -> Self {
        Self {
            ptr: ptr::NonNull::dangling().as_ptr(),
            len: 0,
            cap: 0,
        }
    }

    fn push(&mut self, value: T) {
        if self.len == self.cap {
            self.grow();
        }
        unsafe {
            ptr::write(self.ptr.add(self.len), value);
        }
        self.len += 1;
    }

    fn grow(&mut self) {
        let new_cap = if self.cap == 0 { 4 } else { self.cap * 2 };
        let new_ptr = unsafe {
            std::alloc::realloc(
                self.ptr as *mut u8,
                std::alloc::Layout::array::<T>(self.cap).unwrap(),
                std::mem::size_of::<T>() * new_cap,
            ) as *mut T
        };
        self.ptr = new_ptr;
        self.cap = new_cap;
    }

    fn get(&self, index: usize) -> Option<&T> {
        if index < self.len {
            unsafe { Some(&*self.ptr.add(index)) }
        } else {
            None
        }
    }
}

fn main() {
    let mut v = MyVec::new();
    v.push(1);
    v.push(2);
    v.push(3);

    for i in 0..3 {
        println!("v[{}] = {:?}", i, v.get(i));
    }
}

22.7 unsafe 最佳实践

封装 unsafe 代码

// ✅ 好的做法:用安全 API 封装 unsafe 代码
pub fn safe_split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
    assert!(mid <= slice.len(), "索引越界");
    // unsafe 被封装在安全函数内部
    unsafe { split_at_mut_unchecked(slice, mid) }
}

unsafe fn split_at_mut_unchecked(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
    let len = slice.len();
    let ptr = slice.as_mut_ptr();
    (
        std::slice::from_raw_parts_mut(ptr, mid),
        std::slice::from_raw_parts_mut(ptr.add(mid), len - mid),
    )
}

fn main() {
    let mut data = vec![1, 2, 3, 4, 5];
    let (a, b) = safe_split_at_mut(&mut data, 2);
    println!("a={:?}, b={:?}", a, b);
}

使用 Miri 检查

# 安装 Miri(unsafe 代码检查工具)
rustup +nightly component add miri

# 运行 Miri
cargo +nightly miri test
cargo +nightly miri run

22.8 业务场景示例

性能优化:手动内存管理

use std::alloc::{alloc, dealloc, Layout};

struct Buffer {
    ptr: *mut u8,
    layout: Layout,
}

impl Buffer {
    fn new(size: usize) -> Self {
        let layout = Layout::array::<u8>(size).unwrap();
        let ptr = unsafe { alloc(layout) };
        if ptr.is_null() {
            std::alloc::handle_alloc_error(layout);
        }
        // 清零
        unsafe {
            std::ptr::write_bytes(ptr, 0, size);
        }
        Self { ptr, layout }
    }

    fn as_slice(&self) -> &[u8] {
        unsafe {
            std::slice::from_raw_parts(self.ptr, self.layout.size())
        }
    }
}

impl Drop for Buffer {
    fn drop(&mut self) {
        unsafe {
            dealloc(self.ptr, self.layout);
        }
    }
}

fn main() {
    let buf = Buffer::new(1024);
    println!("缓冲区大小: {} 字节", buf.as_slice().len());
    println!("前10字节: {:?}", &buf.as_slice()[..10]);
}

22.9 本章小结

要点说明
unsafe解锁五种额外能力,不关闭借用检查
裸指针*const T*mut T
FFIextern “C” 调用 C 代码
Union类似 C 的联合体
安全封装用安全 API 包装 unsafe 代码
Miriunsafe 代码检查工具

扩展阅读

  1. Rustonomicon — unsafe 深入
  2. The Unsafe Book — 官方教程
  3. Rust FFI Guide — FFI 详解