Learn Rust Coding · 课时

与不安全 Rust 交互

了解何时以及如何使用 `unsafe` 代码块绕过 Rust 的安全检查,从而实现 FFI 和底层内存操作。

第 3 / 3 课11 个步骤

与不安全 Rust 交互 是 CoddyKit 上的免费 Learn Rust Coding 课时。 这是第 3 节课,共 3 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Learn Rust Coding 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Learn Rust Coding 课程共包含 3 节课。

不安全 Rust:简介

欢迎学习不安全 Rust!Rust 以其内存安全保证而闻名,但在某些特定场景中,您可能需要绕过部分检查。

Rust 中的 unsafe 关键字正是用于此目的。它并不是编写“糟糕”代码的方法,而是用于高级场景的工具,适用于需要更直接控制内存或硬件的情况。

不安全 Rust 的五种超能力

使用 unsafe 块时,您可以执行五种 Rust 编译器通常会阻止的特殊操作:

  • 解引用裸指针:直接访问内存地址。
  • 调用 unsafe 函数或方法:执行带有编译器无法验证的前置条件的函数。
  • 实现 unsafe 特征:声明您的类型遵守特定不变量。
  • 访问或修改可变静态变量:在全局范围共享可变状态,但可能引发数据竞争。
  • 访问 union 的字段:从联合体中读取数据,而该数据对于当前内容可能属于无效类型。

解引用裸指针

裸指针是没有 Rust 通常安全保证的内存地址。您可以从引用(&T、&mut T)创建裸指针,但解引用它们需要使用 unsafe 块。

这意味着您作为程序员,需要负责确保指针有效,并且指向已分配的内存。

fn main() {
  let mut num = 5;
  let r1 = &num as *const i32; // Immutable raw pointer
  let r2 = &mut num as *mut i32; // Mutable raw pointer

  unsafe { // The `unsafe` block starts here
    println!("r1 points to: {}", *r1);
    *r2 = 10; // Modify data through mutable raw pointer
    println!("r2 points to: {}", *r2);
  } // The `unsafe` block ends here

  println!("Num is now: {}", num);
}

调用不安全函数

某些函数会标记为 unsafe fn。这表示该函数具有 Rust 编译器无法保证的前置条件。例如,函数可能要求一个有效的内存地址,但无法验证该地址。

调用此类函数必须放在 unsafe 块中,以表明您作为调用者确保所有前置条件都已满足。

unsafe fn dangerous_operation() {
  println!("This operation could be dangerous if preconditions aren't met!");
}

fn main() {
  println!("Attempting a dangerous operation...");
  unsafe { // Calling an unsafe function requires `unsafe`
    dangerous_operation();
  }
  println!("Operation completed.");
}

可变静态变量

Rust 默认会阻止全局可变状态,以避免数据竞争。不过,您可以使用 static mut 声明可变静态变量。

访问或修改这些变量被视为 unsafe,因为多个线程可能会同时尝试访问它们,从而导致未定义行为。您需要负责进行适当的同步。

static mut COUNTER: i32 = 0; // A mutable static variable

fn add_to_counter(inc: i32) {
  unsafe { // Modifying `static mut` requires `unsafe`
    COUNTER += inc;
  }
}

fn main() {
  add_to_counter(5);
  unsafe { // Reading `static mut` also requires `unsafe`
    println!("COUNTER after first add: {}", COUNTER);
  }

  add_to_counter(10);
  unsafe {
    println!("COUNTER after second add: {}", COUNTER);
  }
}

实现不安全特征

Rust 中的某些特征会标记为 unsafe trait。这表示实现它们时,必须遵守编译器无法检查的某些不变量。

常见示例是 Send 和 Sync 特征,它们与线程安全有关。如果您手动实现 unsafe trait,则必须使用 unsafe impl Trait for Type,并保证其安全属性。

对于大多数初学者来说,您不太常遇到这种情况,因为 Rust 通常会自动处理这些内容,或通过安全抽象来实现。

访问联合体字段

union 是一种特殊类型,在任意时刻可以保存其某个变体的值,但所有变体共享同一个内存位置。它类似于 C 语言中的联合体。

访问 union 的字段需要使用 unsafe 块,因为编译器无法知道当前哪个字段处于活动状态。读取未活动字段可能导致未定义行为或对数据的错误解释。

union Data {
  integer: u32,
  float: f32,
}

fn main() {
  let mut d = Data { integer: 42 }; // Initialize with an integer

  unsafe {
    // Accessing 'integer' is safe here
    println!("Integer value: {}", d.integer);

    // Accessing 'float' is unsafe, as 'integer' was initialized.
    // This would interpret the integer's bits as a float.
    // println!("Float value (unsafe): {}", d.float);
  }

  d.float = 3.14; // Now initialize with a float
  unsafe {
    // Accessing 'float' is safe now
    println!("Float value: {}", d.float);
  }
}

不安全 Rust 的实际场景

虽然应谨慎使用 unsafe,但它对于多项高级编程任务至关重要:

  • 外部函数接口(FFI):与其他语言编写的代码(例如 C/C++ 库)交互。
  • 性能优化:有时,绕过 Rust 的检查可以在关键代码段中带来少量性能提升(但请先进行测量!)。
  • 底层系统编程:编写操作系统、设备驱动程序或嵌入式系统。
  • 构建安全抽象:在不安全原语之上实现安全的数据结构或 API(例如 Vec 的构建方式)。

使用不安全功能时的责任

使用 unsafe 时,您需要手动承担维护 Rust 安全保证的责任。这意味着:

  • 确保内存有效且正确对齐。
  • 处理共享可变状态时防止数据竞争。
  • 避免悬空指针或释放后使用错误。
  • 确保满足所有函数前置条件。

滥用 unsafe 可能导致未定义行为,这是最严重的一类错误,并且可能非常难以调试。

不安全知识检查

以下哪种操作在 Rust 中需要使用 unsafe 块?

回顾:不安全 Rust

您已经学习了不安全 Rust。这是一项强大功能,允许您在特定的高级场景中绕过 Rust 的部分编译时安全检查。

请记住,unsafe 并不会完全关闭借用检查器,而是将内存安全和不变量的责任完全转交给您——程序员。请谨慎并极其小心地使用它,通常将其用于 FFI、底层优化或构建安全抽象。

免费开始

用 AI 导师学习 Rust — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
39
课程
144

常见问题解答

「与不安全 Rust 交互」课时是免费的吗?

是的 — 「与不安全 Rust 交互」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Learn Rust Coding 课程的其余内容,请升级到 CoddyKit PRO。 Learn Rust Coding 课程共包含 3 节课。

「与不安全 Rust 交互」这节课中我会学到什么?

了解何时以及如何使用 `unsafe` 代码块绕过 Rust 的安全检查,从而实现 FFI 和底层内存操作。 你通过在浏览器中直接运行的动手代码来练习 Learn Rust Coding,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Learn Rust Coding 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Learn Rust Coding 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 3 节。

「与不安全 Rust 交互」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Learn Rust Coding 课中编写并运行代码吗?

能。每节 Learn Rust Coding 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 声明式宏(`macro_rules!`)
  2. 过程宏:派生宏与函数宏
  3. 与不安全 Rust 交互
← 返回 Learn Rust Coding