searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Rust不安全代码学习笔记

2025-11-13 09:50:34
2
0

Rust不安全代码学习笔记

Rust 的最大卖点之一是内存安全(memory safety),但有时候,我们需要暂时突破它的限制来完成某些底层操作。unsafe 关键字就是这扇“逃生门”,它允许我们在极少数场景下进行不受检查的内存操作——只要我们保证安全边界依然存在。

1. 裸指针(Raw Pointers)

let mut number = 5;

let r1 = &number as *const i32;   // 不可变裸指针
let r2 = &mut number as *mut i32; // 可变裸指针

unsafe {
    println!("r1 is: {}", *r1);
    println!("r2 is: {}", *r2);
}

裸指针类型是 *const T 和 *mut T。
它们和普通引用的区别在于:

  • 创建它们是安全的;

  • 解引用(*r1)是危险的,必须在 unsafe 块中进行;

  • 它们可以为空(null)、悬垂(dangling),或指向无效内存。
    例如:

    let address = 0x012345usize;
    let r = address as *const i32;
    unsafe {
    println!("r: {}", *r); // 这会导致未定义行为!
    }
    

    这里我们伪造了一个地址——Rust 无法验证它是否有效。
    这正是 Rust 要求 unsafe 的原因:它让风险可见。

2. unsafe fn 与 unsafe 块

unsafe fn dangerous() {}

fn test3() {
    unsafe {
        dangerous();
    }
}

unsafe fn 表示调用它的地方必须显式标明 unsafe。
这不意味着函数内部必然不安全,而是提醒调用者:你需要自己保证安全前提成立。

3. 安全抽象的构建

标准库中的 split_at_mut 函数可以安全地把一个可变切片拆分成两个不重叠的切片:

let mut v = vec![1, 2, 3, 4, 5];
let r = &mut v[..];
let (a, b) = r.split_at_mut(3);

assert_eq!(a, &mut [1, 2, 3]);
assert_eq!(b, &mut [4, 5]);

如果我们手动实现,最直觉的写法会被编译器拒绝:

fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
    let len = slice.len();
    assert!(mid <= len);
    (&mut slice[..mid], &mut slice[mid..]) // ❌ 报错:同时存在两个可变借用
}

编译器无法证明这两个切片不会重叠。
但我们程序员知道它们不会,于是可以使用裸指针来“手动保证”:

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 {
        (    // 这里的from_raw_parts_mut就是unsafe fn
             // 因为它默认了ptr是有效的指针,而且还在指针上做地址偏移,去访问不一定有效的空间
            slice::from_raw_parts_mut(ptr, mid),
            slice::from_raw_parts_mut(ptr.add(mid), len - mid),
        )
    }
}

4. 全局可变变量(static mut)

static mut COUNTER: u32 = 0;

fn add_to_count(inc: u32) {
    unsafe {
        COUNTER += inc;
    }
}

fn main() {
    unsafe {
        println!("before: {}", COUNTER);
    }
    add_to_count(3);
    unsafe {
        println!("after: {}", COUNTER);
    }
}

static mut 表示一个全局的可变变量。
这非常危险,因为它绕过了 Rust 的所有所有权检查:任何线程都能修改它。因此读写 static mut 必须放在 unsafe 中。

0条评论
作者已关闭评论
陈****然
9文章数
0粉丝数
陈****然
9 文章 | 0 粉丝
陈****然
9文章数
0粉丝数
陈****然
9 文章 | 0 粉丝
原创

Rust不安全代码学习笔记

2025-11-13 09:50:34
2
0

Rust不安全代码学习笔记

Rust 的最大卖点之一是内存安全(memory safety),但有时候,我们需要暂时突破它的限制来完成某些底层操作。unsafe 关键字就是这扇“逃生门”,它允许我们在极少数场景下进行不受检查的内存操作——只要我们保证安全边界依然存在。

1. 裸指针(Raw Pointers)

let mut number = 5;

let r1 = &number as *const i32;   // 不可变裸指针
let r2 = &mut number as *mut i32; // 可变裸指针

unsafe {
    println!("r1 is: {}", *r1);
    println!("r2 is: {}", *r2);
}

裸指针类型是 *const T 和 *mut T。
它们和普通引用的区别在于:

  • 创建它们是安全的;

  • 解引用(*r1)是危险的,必须在 unsafe 块中进行;

  • 它们可以为空(null)、悬垂(dangling),或指向无效内存。
    例如:

    let address = 0x012345usize;
    let r = address as *const i32;
    unsafe {
    println!("r: {}", *r); // 这会导致未定义行为!
    }
    

    这里我们伪造了一个地址——Rust 无法验证它是否有效。
    这正是 Rust 要求 unsafe 的原因:它让风险可见。

2. unsafe fn 与 unsafe 块

unsafe fn dangerous() {}

fn test3() {
    unsafe {
        dangerous();
    }
}

unsafe fn 表示调用它的地方必须显式标明 unsafe。
这不意味着函数内部必然不安全,而是提醒调用者:你需要自己保证安全前提成立。

3. 安全抽象的构建

标准库中的 split_at_mut 函数可以安全地把一个可变切片拆分成两个不重叠的切片:

let mut v = vec![1, 2, 3, 4, 5];
let r = &mut v[..];
let (a, b) = r.split_at_mut(3);

assert_eq!(a, &mut [1, 2, 3]);
assert_eq!(b, &mut [4, 5]);

如果我们手动实现,最直觉的写法会被编译器拒绝:

fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
    let len = slice.len();
    assert!(mid <= len);
    (&mut slice[..mid], &mut slice[mid..]) // ❌ 报错:同时存在两个可变借用
}

编译器无法证明这两个切片不会重叠。
但我们程序员知道它们不会,于是可以使用裸指针来“手动保证”:

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 {
        (    // 这里的from_raw_parts_mut就是unsafe fn
             // 因为它默认了ptr是有效的指针,而且还在指针上做地址偏移,去访问不一定有效的空间
            slice::from_raw_parts_mut(ptr, mid),
            slice::from_raw_parts_mut(ptr.add(mid), len - mid),
        )
    }
}

4. 全局可变变量(static mut)

static mut COUNTER: u32 = 0;

fn add_to_count(inc: u32) {
    unsafe {
        COUNTER += inc;
    }
}

fn main() {
    unsafe {
        println!("before: {}", COUNTER);
    }
    add_to_count(3);
    unsafe {
        println!("after: {}", COUNTER);
    }
}

static mut 表示一个全局的可变变量。
这非常危险,因为它绕过了 Rust 的所有所有权检查:任何线程都能修改它。因此读写 static mut 必须放在 unsafe 中。

文章来自个人专栏
文章 | 订阅
0条评论
作者已关闭评论
作者已关闭评论
0
0