Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Rust Bootstrap Course for C/C++ Programmers / 面向 C/C++ 程序员的 Rust 入门强化课程

Course Overview / 课程概览

  • Course overview / 课程包含以下内容
    • The case for Rust (from both C and C++ perspectives) / 为什么选择 Rust(从 C 和 C++ 两个角度出发)
    • Local installation / 本地安装
    • Types, functions, control flow, pattern matching / 类型、函数、控制流、模式匹配
    • Modules, cargo / 模块与 Cargo
    • Traits, generics / Trait 与泛型
    • Collections, error handling / 集合与错误处理
    • Closures, memory management, lifetimes, smart pointers / 闭包、内存管理、生命周期、智能指针
    • Concurrency / 并发
    • Unsafe Rust, including Foreign Function Interface (FFI) / Unsafe Rust,包括外部函数接口(FFI)
    • no_std and embedded Rust essentials for firmware teams / 面向固件团队的 no_std 与嵌入式 Rust 核心内容
    • Case studies: real-world C++ to Rust translation patterns / 案例研究:真实世界中的 C++ 到 Rust 迁移模式
  • We’ll not cover async Rust in this course - see the companion Async Rust Training for a full treatment of futures, executors, Pin, tokio, and production async patterns
  • 本课程讲解 async Rust;如需系统学习 futures、执行器、Pin、tokio 与生产级异步模式,请参考配套的 Async Rust Training

Self-Study Guide / 自学指南

This material works both as an instructor-led course and for self-study. If you’re working through it on your own, here’s how to get the most out of it:

本材料既适合讲师授课,也适合自学。如果你打算独立学习,下面的建议可以帮助你获得更好的学习效果:

Pacing recommendations / 学习节奏建议:

Chapters / 章节Topic / 主题Suggested Time / 建议时间Checkpoint / 检查点
1-4Setup, types, control flow / 环境、类型、控制流1 day / 1 天You can write a CLI temperature converter / 你可以写出一个命令行温度转换器
5-7Data structures, ownership / 数据结构、所有权1-2 days / 1-2 天You can explain why let s2 = s1 invalidates s1 / 你可以解释为什么 let s2 = s1 会使 s1 失效
8-9Modules, error handling / 模块、错误处理1 day / 1 天You can create a multi-file project that propagates errors with ? / 你可以创建一个多文件项目并用 ? 传播错误
10-12Traits, generics, closures / Trait、泛型、闭包1-2 days / 1-2 天You can write a generic function with trait bounds / 你可以写出带 trait 约束的泛型函数
13-14Concurrency, unsafe/FFI / 并发、unsafe/FFI1 day / 1 天You can write a thread-safe counter with Arc<Mutex<T>> / 你可以用 Arc<Mutex<T>> 写出线程安全计数器
15-16Deep dives / 深入专题At your own pace / 自定节奏Reference material - read when relevant / 作为参考材料,在需要时查阅
17-19Best practices & reference / 最佳实践与参考At your own pace / 自定节奏Consult as you write real code / 在实际写代码时按需查阅

How to use the exercises / 如何使用练习:

  • Every chapter has hands-on exercises marked with difficulty: 🟢 Starter, 🟡 Intermediate, 🔶 Challenge / 每章都包含带难度标记的动手练习:🟢 入门、🟡 中级、🔶 挑战
  • Always try the exercise before expanding the solution. Struggling with the borrow checker is part of learning - the compiler’s error messages are your teacher / 总是先做题,再看答案。 与借用检查器“拉扯”是学习的一部分,编译器的报错就是你的老师
  • If you’re stuck for more than 15 minutes, expand the solution, study it, then close it and try again from scratch / 如果卡住超过 15 分钟,就先看答案、理解思路,再关闭答案重新独立完成
  • The Rust Playground lets you run code without a local install / Rust Playground 允许你无需本地安装就运行代码

When you hit a wall / 遇到难点时:

  • Read the compiler error message carefully - Rust’s errors are exceptionally helpful / 仔细阅读编译器错误信息,Rust 的错误提示通常非常有帮助
  • Re-read the relevant section; concepts like ownership (ch7) often click on the second pass / 重读相关章节,像所有权这类概念常常在第二遍时真正理解
  • The Rust standard library docs are excellent - search for any type or method / Rust 标准库文档 非常优秀,任何类型或方法都值得查阅
  • For async patterns, see the companion Async Rust Training / 如需学习异步模式,请参考配套的 Async Rust Training

Table of Contents / 目录

Part I - Foundations / 第一部分:基础

1. Introduction and Motivation / 1. 引言与动机

2. Getting Started / 2. 快速开始

3. Basic Types and Variables / 3. 基础类型与变量

4. Control Flow / 4. 控制流

5. Data Structures and Collections / 5. 数据结构与集合

6. Pattern Matching and Enums / 6. 模式匹配与枚举

7. Ownership and Memory Management / 7. 所有权与内存管理

8. Modules and Crates / 8. 模块与 Crate

9. Error Handling / 9. 错误处理

10. Traits and Generics / 10. Trait 与泛型

11. Type System Advanced Features / 11. 类型系统高级特性

12. Functional Programming / 12. 函数式编程

13. Concurrency / 13. 并发

14. Unsafe Rust and FFI / 14. Unsafe Rust 与 FFI

Part II - Deep Dives / 第二部分:深入专题

15. no_std - Rust for Bare Metal / 15. no_std:面向裸机的 Rust

16. Case Studies: Real-World C++ to Rust Translation / 16. 案例研究:真实世界中的 C++ 到 Rust 迁移

Part III - Best Practices & Reference / 第三部分:最佳实践与参考

17. Best Practices / 17. 最佳实践

18. C++ to Rust Semantic Deep Dives / 18. C++ 到 Rust 的语义深入对比

19. Rust Macros / 19. Rust 宏

Speaker intro and general approach / 讲师介绍与整体方法

What you’ll learn / 你将学到: Course structure, the interactive format, and how familiar C/C++ concepts map to Rust equivalents. This chapter sets expectations and gives you a roadmap for the rest of the book.

课程结构、互动式学习方式,以及熟悉的 C/C++ 概念如何映射到 Rust 对应概念。本章会帮助你建立预期,并为后续内容提供学习路线图。

  • Speaker intro / 讲师介绍
    • Principal Firmware Architect in Microsoft SCHIE (Silicon and Cloud Hardware Infrastructure Engineering) team / Microsoft SCHIE(Silicon and Cloud Hardware Infrastructure Engineering)团队首席固件架构师
    • Industry veteran with expertise in security, systems programming (firmware, operating systems, hypervisors), CPU and platform architecture, and C++ systems / 在安全、系统编程(固件、操作系统、虚拟机监控器)、CPU 与平台架构以及 C++ 系统方面经验丰富
    • Started programming in Rust in 2017 (@AWS EC2), and have been in love with the language ever since / 2017 年在 AWS EC2 开始使用 Rust,此后长期深度投入
  • This course is intended to be as interactive as possible / 本课程尽量采用高互动式教学
    • Assumption: You know C, C++, or both / 前提:你熟悉 C、C++,或者两者都熟悉
    • Examples are deliberately designed to map familiar concepts to Rust equivalents / 示例会刻意把熟悉的 C/C++ 概念映射到 Rust 对应概念
    • Please feel free to ask clarifying questions at any point of time / 任何时候都欢迎提出澄清性问题
  • Speaker is looking forward to continued engagement with teams / 也期待后续继续与团队深入交流

The case for Rust / 为什么选择 Rust

Want to skip straight to code? / 想直接看代码? Jump to Show me some code / 直接跳到“给我看看代码”

Whether you’re coming from C or C++, the core pain points are the same: memory safety bugs that compile cleanly but crash, corrupt, or leak at runtime.

无论你来自 C 还是 C++,核心痛点其实都一样:很多内存安全问题可以顺利编译通过,但会在运行时导致崩溃、数据损坏或内存泄漏。

  • Over 70% of CVEs are caused by memory safety issues - buffer overflows, dangling pointers, use-after-free / 超过 70% 的 CVE 都源于内存安全问题,例如缓冲区溢出、悬垂指针、释放后使用
  • C++ shared_ptr, unique_ptr, RAII, and move semantics are steps in the right direction, but they are bandaids, not cures - they leave use-after-move, reference cycles, iterator invalidation, and exception safety gaps wide open / C++ 的 shared_ptrunique_ptr、RAII 和移动语义是在往正确方向努力,但它们更像创可贴,而不是根治方案,因为它们仍然留下了 move 后使用、引用环、迭代器失效和异常安全等缺口
  • Rust provides the performance you rely on from C/C++, but with compile-time guarantees for safety / Rust 保留了你依赖的 C/C++ 性能,同时提供了编译期安全保证

Deep dive / 深入阅读: See Why C/C++ Developers Need Rust / 为什么 C/C++ 开发者需要 Rust for concrete vulnerability examples, the complete list of what Rust eliminates, and why C++ smart pointers aren’t enough.

若想看更具体的漏洞示例、Rust 究竟消除了哪些问题,以及为什么 C++ 智能指针仍不够,请阅读 Why C/C++ Developers Need Rust / 为什么 C/C++ 开发者需要 Rust


How does Rust address these issues? / Rust 如何解决这些问题?

Buffer overflows and bounds violations / 缓冲区溢出与越界访问

  • All Rust arrays, slices, and strings have explicit bounds associated with them. The compiler inserts checks to ensure that any bounds violation results in a runtime crash (panic in Rust terms) - never undefined behavior / Rust 的数组、切片和字符串都带有明确边界。编译器会插入检查,确保任何越界访问只会导致运行时崩溃(Rust 中称为 panic),而不会变成未定义行为

Dangling pointers and references / 悬垂指针与悬垂引用

  • Rust introduces lifetimes and borrow checking to eliminate dangling references at compile time / Rust 通过生命周期和借用检查,在编译期消除悬垂引用
  • No dangling pointers, no use-after-free - the compiler simply won’t let you / 不会出现悬垂指针,也不会出现释放后使用,因为编译器根本不允许这种代码通过

Use-after-move / move 后继续使用

  • Rust’s ownership system makes moves destructive - once you move a value, the compiler refuses to let you use the original. No zombie objects, no “valid but unspecified state” / Rust 的所有权系统让 move 成为破坏性操作:一旦值被移动,编译器就会拒绝你继续使用原值。不会有“僵尸对象”,也不会有“有效但状态未指定”的模糊状态

Resource management / 资源管理

  • Rust’s Drop trait is RAII done right - the compiler automatically frees resources when they go out of scope, and prevents use-after-move which C++ RAII cannot / Rust 的 Drop trait 可以看作“真正做对了的 RAII”:资源离开作用域时自动释放,同时还能阻止 move 后继续使用,这一点是 C++ RAII 做不到的
  • No Rule of Five needed (no copy ctor, move ctor, copy assign, move assign, destructor to define) / 不再需要 Rule of Five(不需要手写复制构造、移动构造、复制赋值、移动赋值和析构函数)

Error handling / 错误处理

  • Rust has no exceptions. All errors are values (Result<T, E>), making error handling explicit and visible in the type signature / Rust 没有异常机制。所有错误都是值(Result<T, E>),因此错误处理是显式的,并直接体现在类型签名中

Iterator invalidation / 迭代器失效

  • Rust’s borrow checker forbids modifying a collection while iterating over it. You simply cannot write the bugs that plague C++ codebases: / Rust 的借用检查器禁止在遍历集合时修改该集合。很多困扰 C++ 代码库的 bug,在 Rust 中根本写不出来:
#![allow(unused)]
fn main() {
// Rust equivalent of erase-during-iteration: retain()
pending_faults.retain(|f| f.id != fault_to_remove.id);

// Or: collect into a new Vec (functional style)
let remaining: Vec<_> = pending_faults
    .into_iter()
    .filter(|f| f.id != fault_to_remove.id)
    .collect();
}

Data races / 数据竞争

  • The type system prevents data races at compile time through the Send and Sync traits / 类型系统会通过 SendSync trait 在编译期阻止数据竞争

Memory Safety Visualization / 内存安全可视化

Rust Ownership - Safe by Design / Rust 所有权:设计上就是安全的

#![allow(unused)]
fn main() {
fn safe_rust_ownership() {
    // Move is destructive: original is gone
    let data = vec![1, 2, 3];
    let data2 = data;           // Move happens
    // data.len();              // Compile error: value used after move
    
    // Borrowing: safe shared access
    let owned = String::from("Hello, World!");
    let slice: &str = &owned;  // Borrow - no allocation
    println!("{}", slice);     // Always safe
    
    // No dangling references possible
    /*
    let dangling_ref;
    {
        let temp = String::from("temporary");
        dangling_ref = &temp;  // Compile error: temp doesn't live long enough
    }
    */
}
}
graph TD
    A[Rust Ownership Safety] --> B[Destructive Moves]
    A --> C[Automatic Memory Management]
    A --> D[Compile-time Lifetime Checking]
    A --> E[No Exceptions - Result Types]
    
    B --> B1["Use-after-move is compile error"]
    B --> B2["No zombie objects"]
    
    C --> C1["Drop trait = RAII done right"]
    C --> C2["No Rule of Five needed"]
    
    D --> D1["Borrow checker prevents dangling"]
    D --> D2["References always valid"]
    
    E --> E1["Result<T,E> - errors in types"]
    E --> E2["? operator for propagation"]
    
    style A fill:#51cf66,color:#000
    style B fill:#91e5a3,color:#000
    style C fill:#91e5a3,color:#000
    style D fill:#91e5a3,color:#000
    style E fill:#91e5a3,color:#000

Memory Layout: Rust References / 内存布局:Rust 引用

graph TD
    RM1[Stack] --> RP1["&i32 ref"]
    RM2[Stack/Heap] --> RV1["i32 value = 42"]
    RP1 -.->|"Safe reference - Lifetime checked"| RV1
    RM3[Borrow Checker] --> RC1["Prevents dangling refs at compile time"]
    
    style RC1 fill:#51cf66,color:#000
    style RP1 fill:#91e5a3,color:#000

Box<T> Heap Allocation Visualization / Box<T> 堆分配示意

#![allow(unused)]
fn main() {
fn box_allocation_example() {
    // Stack allocation
    let stack_value = 42;
    
    // Heap allocation with Box
    let heap_value = Box::new(42);
    
    // Moving ownership
    let moved_box = heap_value;
    // heap_value is no longer accessible
}
}
graph TD
    subgraph "Stack Frame"
        SV["stack_value: 42"]
        BP["heap_value: Box<i32>"]
        BP2["moved_box: Box<i32>"]
    end
    
    subgraph "Heap"
        HV["42"]
    end
    
    BP -->|"Owns"| HV
    BP -.->|"Move ownership"| BP2
    BP2 -->|"Now owns"| HV
    
    subgraph "After Move"
        BP_X["heap_value: [WARNING] MOVED"]
        BP2_A["moved_box: Box<i32>"]
    end
    
    BP2_A -->|"Owns"| HV
    
    style BP_X fill:#ff6b6b,color:#000
    style HV fill:#91e5a3,color:#000
    style BP2_A fill:#51cf66,color:#000

Slice Operations Visualization / 切片操作示意

#![allow(unused)]
fn main() {
fn slice_operations() {
    let data = vec![1, 2, 3, 4, 5, 6, 7, 8];
    
    let full_slice = &data[..];        // [1,2,3,4,5,6,7,8]
    let partial_slice = &data[2..6];   // [3,4,5,6]
    let from_start = &data[..4];       // [1,2,3,4]
    let to_end = &data[3..];           // [4,5,6,7,8]
}
}
graph TD
    V["Vec: [1, 2, 3, 4, 5, 6, 7, 8]"]
    V --> FS["&data[..] -> all elements"]
    V --> PS["&data[2..6] -> [3, 4, 5, 6]"]
    V --> SS["&data[..4] -> [1, 2, 3, 4]"]
    V --> ES["&data[3..] -> [4, 5, 6, 7, 8]"]
    
    style V fill:#e3f2fd,color:#000
    style FS fill:#91e5a3,color:#000
    style PS fill:#91e5a3,color:#000
    style SS fill:#91e5a3,color:#000
    style ES fill:#91e5a3,color:#000

Other Rust USPs and features / Rust 的其他独特优势与特性

  • No data races between threads (compile-time Send/Sync checking) / 线程之间不会出现数据竞争(Send/Sync 在编译期检查)
  • No use-after-move (unlike C++ std::move which leaves zombie objects) / 没有 move 后继续使用(不同于 C++ std::move 可能留下“僵尸对象”)
  • No uninitialized variables / 不存在未初始化变量
    • All variables must be initialized before use / 所有变量都必须初始化后才能使用
  • No trivial memory leaks / 不会轻易出现“低级”内存泄漏
    • Drop trait = RAII done right, no Rule of Five needed / Drop trait 可视为“真正做对了的 RAII”,无需 Rule of Five
    • Compiler automatically releases memory when it goes out of scope / 变量离开作用域时,编译器自动释放内存
  • No forgotten locks on mutexes / 不会忘记正确管理 mutex 锁
    • Lock guards are the only way to access the data (Mutex<T> wraps the data, not the access) / 锁保护对象是访问数据的唯一方式(Mutex<T> 包装的是数据本身,而不是访问路径)
  • No exception handling complexity / 没有异常处理复杂性
    • Errors are values (Result<T, E>), visible in function signatures, propagated with ? / 错误是值(Result<T, E>),体现在函数签名中,并通过 ? 传播
  • Excellent support for type inference, enums, pattern matching, zero cost abstractions / 对类型推断、枚举、模式匹配和零成本抽象有很强支持
  • Built-in support for dependency management, building, testing, formatting, linting / 内建依赖管理、构建、测试、格式化和 lint 支持
    • cargo replaces make/CMake + lint + test frameworks / cargo 可以取代 make/CMake 以及一整套 lint 和测试框架组合

Quick Reference: Rust vs C/C++ / 速查:Rust 与 C/C++ 对比

Concept / 概念CC++RustKey Difference / 关键差异
Memory management / 内存管理malloc()/free()unique_ptr, shared_ptrBox<T>, Rc<T>, Arc<T>Automatic, no cycles / 自动管理,避免引用环问题
Arrays / 数组int arr[10]std::vector<T>, std::array<T>Vec<T>, [T; N]Bounds checking by default / 默认带边界检查
Strings / 字符串char* with \0std::string, string_viewString, &strUTF-8 guaranteed, lifetime-checked / 默认 UTF-8,并带生命周期检查
References / 引用int* ptrT&, T&& (move)&T, &mut TBorrow checking, lifetimes / 借用检查与生命周期
Polymorphism / 多态Function pointers / 函数指针Virtual functions, inheritance / 虚函数、继承Traits, trait objects / trait 与 trait objectComposition over inheritance / 组合优于继承
Generic programming / 泛型编程Macros (void*)TemplatesGenerics + trait boundsBetter error messages / 错误信息更友好
Error handling / 错误处理Return codes, errnoExceptions, std::optionalResult<T, E>, Option<T>No hidden control flow / 没有隐藏控制流
NULL/null safety / 空值安全ptr == NULLnullptr, std::optional<T>Option<T>Forced null checking / 强制显式处理空值
Thread safety / 线程安全Manual (pthreads) / 手工保证Manual synchronization / 手工同步Compile-time guarantees / 编译期保证Data races impossible / 数据竞争在结构上不可能
Build system / 构建系统Make, CMakeCMake, Make, etc.CargoIntegrated toolchain / 一体化工具链
Undefined behavior / 未定义行为Runtime crashes / 运行时崩溃Subtle UB (signed overflow, aliasing) / 隐蔽 UB(如有符号溢出、别名问题)Compile-time errors / 编译期报错Safety guaranteed / 安全性更有保障

1.1 Why C/C++ Developers Need Rust / 1.1 为什么 C/C++ 开发者需要 Rust

What you’ll learn / 你将学到:

  • The full list of problems Rust eliminates — memory safety, undefined behavior, data races, and more / Rust 消除了哪些问题:内存安全、未定义行为、数据竞争等完整清单
  • Why shared_ptr, unique_ptr, and other C++ mitigations are bandaids, not solutions / 为什么 shared_ptrunique_ptr 及其他 C++ 缓解手段只是“创可贴”,而非根本解决方案
  • Concrete C and C++ vulnerability examples that are structurally impossible in safe Rust / 具体的 C 和 C++ 漏洞示例,以及为什么它们在安全 Rust 中在结构上是不可能出现的

Want to skip straight to code? / 想直接看代码? Jump to Show me some code / 少说多练:先看代码

What Rust Eliminates — The Complete List / Rust 消除了什么 —— 完整清单

Before diving into examples, here’s the executive summary. Safe Rust structurally prevents every issue in this list — not through discipline, tooling, or code review, but through the type system and compiler:

在深入查看示例之前,先看下这份概要总结。安全 Rust 在结构上防止了下表中的每一个问题 —— 这不是通过开发者的自觉、工具或代码审查来实现的,而是通过类型系统和编译器强制保证的:

Eliminated Issue / 消除的问题CC++How Rust Prevents It / Rust 如何预防
Buffer overflows / underflows / 缓冲区溢出 / 欠载All arrays, slices, and strings carry bounds; indexing is checked at runtime / 所有数组、切片和字符串都带有边界信息;索引在运行时会进行检查
Memory leaks (no GC needed) / 内存泄漏(无需 GC)Drop trait = RAII done right; automatic cleanup, no Rule of Five / Drop trait = 做对了的 RAII;自动清理,无需 Rule of Five
Dangling pointers / 悬垂指针Lifetime system proves references outlive their referent at compile time / 生命周期系统在编译期证明引用的存活时间长于其指向的对象
Use-after-free / 释放后使用Ownership system makes this a compile error / 所有权系统让这种行为成为编译错误
Use-after-move / move 后继续使用Moves are destructive — the original binding ceases to exist / 移动是破坏性的 —— 原始绑定将不复存在
Uninitialized variables / 未初始化变量All variables must be initialized before use; compiler enforces it / 所有变量必须在初始化后才能使用;由编译器强制执行
Integer overflow / underflow UB / 整数溢出 / 欠载导致的未定义行为Debug builds panic on overflow; release builds wrap (defined behavior either way) / 调试编译在溢出时会 panic;发布编译会回绕(无论哪种都是定义的行为)
NULL pointer dereferences / SEGVs / 空指针解引用 / 段错误No null pointers; Option<T> forces explicit handling / 不存在空指针;Option<T> 强制要求显式处理
Data races / 数据竞争Send/Sync traits + borrow checker make data races a compile error / Send/Sync trait + 借用检查器让数据竞争成为编译错误
Uncontrolled side-effects / 不受控的副作用Immutability by default; mutation requires explicit mut / 默认不可变;修改需要显式使用 mut
No inheritance (better maintainability) / 无继承(更好的可维护性)Traits + composition replace class hierarchies; promotes reuse without coupling / Trait + 组合取代类继承层次;促进复用且无需耦合
No exceptions; predictable control flow / 无异常;可预测的控制流Errors are values (Result<T, E>); impossible to ignore, no hidden throw paths / 错误即是值(Result<T, E>); 无法被忽略,没有隐藏的 throw 路径
Iterator invalidation / 迭代器失效Borrow checker forbids mutating a collection while iterating / 借用检查器禁止在迭代集合时对其进行修改
Reference cycles / leaked finalizers / 引用环 / 内存泄漏的收尾器Ownership is tree-shaped; Rc cycles are opt-in and catchable with Weak / 所有权是树状的;Rc 环是可选的,且可以用 Weak 捕捉
No forgotten mutex unlocks / 不会忘记互斥锁解锁Mutex<T> wraps the data; lock guard is the only way to access it / Mutex<T> 包装了数据;锁保护对象是访问数据的唯一方式
Undefined behavior (general) / 未定义行为(通用)Safe Rust has zero undefined behavior; unsafe blocks are explicit and auditable / 安全 Rust 未定义行为;unsafe 块是显式的且可审计的

Bottom line / 核心结论: These aren’t aspirational goals enforced by coding standards. They are compile-time guarantees. If your code compiles, these bugs cannot exist.

核心结论: 这些并不是靠编码规范来维持的愿景。它们是编译期的强制保证。如果你的代码能编译通过,这些 bug 就不可能存在。


The Problems Shared by C and C++ / C 与 C++ 共同存在的问题

Want to skip the examples? / 想跳过示例? Jump to How Rust Addresses All of This / 直接跳到“Rust 如何解决这一切” or straight to Show me some code / 或直接看代码

Both languages share a core set of memory safety problems that are the root cause of over 70% of CVEs (Common Vulnerabilities and Exposures):

这两种语言都存在一系列核心的内存安全问题,这些问题是导致超过 70% 的 CVE(常见漏洞与披露)的根源:

Buffer overflows / 缓冲区溢出

C arrays, pointers, and strings have no intrinsic bounds. It is trivially easy to exceed them:

C 数组、指针和字符串没有固有的边界。越界访问非常容易发生:

#include <stdlib.h>
#include <string.h>

void buffer_dangers() {
    char buffer[10];
    strcpy(buffer, "This string is way too long!");  // Buffer overflow / 缓冲区溢出

    int arr[5] = {1, 2, 3, 4, 5};
    int *ptr = arr;           // Loses size information / 丢失大小信息
    ptr[10] = 42;             // No bounds check — undefined behavior / 无边界检查 —— 未定义行为
}

In C++, std::vector::operator[] still performs no bounds checking. Only .at() does — and who catches the exception?

在 C++ 中,std::vector::operator[] 仍然不执行边界检查。只有 .at() 会进行检查 —— 但又有谁会去专门捕获那个异常呢?

Dangling pointers and use-after-free / 悬垂指针与释放后使用

int *bar() {
    int i = 42;
    return &i;    // Returns address of stack variable — dangling! / 返回栈变量地址 —— 悬垂了!
}

void use_after_free() {
    char *p = (char *)malloc(20);
    free(p);
    *p = '\0';   // Use after free — undefined behavior / 释放后使用 —— 未定义行为
}

Uninitialized variables and undefined behavior / 未初始化变量与未定义行为

C and C++ both allow uninitialized variables. The resulting values are indeterminate, and reading them is undefined behavior:

C 和 C++ 都允许存在未初始化的变量。其结果值是不确定的,读取它们属于未定义行为:

int x;               // Uninitialized / 未初始化
if (x > 0) { ... }  // UB — x could be anything / 未定义行为 —— x 可能是任何值

Integer overflow is defined in C for unsigned types but undefined for signed types. In C++, signed overflow is also undefined behavior. Both compilers can and do exploit this for “optimizations” that break programs in surprising ways.

在 C 语言中,无符号类型的整数溢出是有定义的,但有符号类型的溢出是未定义的。在 C++ 中,有符号溢出同样是未定义行为。编译器能够也确实会利用这一点进行“优化”,从而以令人惊讶的方式让程序崩溃。

NULL pointer dereferences / 空指针解引用

int *ptr = NULL;
*ptr = 42;           // SEGV — but the compiler won't stop you

In C++, std::optional<T> helps but is verbose and often bypassed with .value() which throws.

The visualization: shared problems

graph TD
    ROOT["C/C++ Memory Safety Issues"] --> BUF["Buffer Overflows"]
    ROOT --> DANGLE["Dangling Pointers"]
    ROOT --> UAF["Use-After-Free"]
    ROOT --> UNINIT["Uninitialized Variables"]
    ROOT --> NULL["NULL Dereferences"]
    ROOT --> UB["Undefined Behavior"]
    ROOT --> RACE["Data Races"]

    BUF --> BUF1["No bounds on arrays/pointers"]
    DANGLE --> DANGLE1["Returning stack addresses"]
    UAF --> UAF1["Reusing freed memory"]
    UNINIT --> UNINIT1["Indeterminate values"]
    NULL --> NULL1["No forced null checks"]
    UB --> UB1["Signed overflow, aliasing"]
    RACE --> RACE1["No compile-time safety"]

    style ROOT fill:#ff6b6b,color:#000
    style BUF fill:#ffa07a,color:#000
    style DANGLE fill:#ffa07a,color:#000
    style UAF fill:#ffa07a,color:#000
    style UNINIT fill:#ffa07a,color:#000
    style NULL fill:#ffa07a,color:#000
    style UB fill:#ffa07a,color:#000
    style RACE fill:#ffa07a,color:#000

C++ Adds More Problems on Top

C audience: You can skip ahead to How Rust Addresses These Issues if you don’t use C++.

Want to skip straight to code? Jump to Show me some code

C++ introduced smart pointers, RAII, move semantics, and exceptions to address C’s problems. These are bandaids, not cures — they shift the failure mode from “crash at runtime” to “subtler bug at runtime”:

unique_ptr and shared_ptr — bandaids, not solutions

C++ smart pointers are a significant improvement over raw malloc/free, but they don’t solve the underlying problems:

C++ MitigationWhat It FixesWhat It Doesn’t Fix
std::unique_ptrPrevents leaks via RAIIUse-after-move still compiles; leaves a zombie nullptr
std::shared_ptrShared ownershipReference cycles leak silently; weak_ptr discipline is manual
std::optionalReplaces some null use.value() throws if empty — hidden control flow
std::string_viewAvoids copiesDangling if the source string is freed — no lifetime checking
Move semanticsEfficient transfersMoved-from objects are in a “valid but unspecified state” — UB waiting to happen
RAIIAutomatic cleanupRequires the Rule of Five to get right; one mistake breaks everything
// unique_ptr: use-after-move compiles cleanly
std::unique_ptr<int> ptr = std::make_unique<int>(42);
std::unique_ptr<int> ptr2 = std::move(ptr);
std::cout << *ptr;  // Compiles! Undefined behavior at runtime.
                     // In Rust, this is a compile error: "value used after move"
// shared_ptr: reference cycles leak silently
struct Node {
    std::shared_ptr<Node> next;
    std::shared_ptr<Node> parent;  // Cycle! Destructor never called.
};
auto a = std::make_shared<Node>();
auto b = std::make_shared<Node>();
a->next = b;
b->parent = a;  // Memory leak — ref count never reaches 0
                 // In Rust, Rc<T> + Weak<T> makes cycles explicit and breakable

Use-after-move — the silent killer

C++ std::move is not a move — it’s a cast. The original object remains in a “valid but unspecified state”. The compiler lets you keep using it:

auto vec = std::make_unique<std::vector<int>>({1, 2, 3});
auto vec2 = std::move(vec);
vec->size();  // Compiles! But dereferencing nullptr — crash at runtime

In Rust, moves are destructive. The original binding is gone:

#![allow(unused)]
fn main() {
let vec = vec![1, 2, 3];
let vec2 = vec;           // Move — vec is consumed
// vec.len();             // Compile error: value used after move
}

Iterator invalidation — real bugs from production C++

These aren’t contrived examples — they represent real bug patterns found in large C++ codebases:

// BUG 1: erase without reassigning iterator (undefined behavior)
while (it != pending_faults.end()) {
    if (*it != nullptr && (*it)->GetId() == fault->GetId()) {
        pending_faults.erase(it);   // ← iterator invalidated!
        removed_count++;            //   next loop uses dangling iterator
    } else {
        ++it;
    }
}
// Fix: it = pending_faults.erase(it);
// BUG 2: index-based erase skips elements
for (auto i = 0; i < entries.size(); i++) {
    if (config_status == ConfigDisable::Status::Disabled) {
        entries.erase(entries.begin() + i);  // ← shifts elements
    }                                         //   i++ skips the shifted one
}
// BUG 3: one erase path correct, the other isn't
while (it != incomplete_ids.end()) {
    if (current_action == nullptr) {
        incomplete_ids.erase(it);  // ← BUG: iterator not reassigned
        continue;
    }
    it = incomplete_ids.erase(it); // ← Correct path
}

These compile without any warning. In Rust, the borrow checker makes all three a compile error — you cannot mutate a collection while iterating over it, period.

Exception safety and the dynamic_cast/new pattern

Modern C++ codebases still lean heavily on patterns that have no compile-time safety:

// Typical C++ factory pattern — every branch is a potential bug
DriverBase* driver = nullptr;
if (dynamic_cast<ModelA*>(device)) {
    driver = new DriverForModelA(framework);
} else if (dynamic_cast<ModelB*>(device)) {
    driver = new DriverForModelB(framework);
}
// What if driver is still nullptr? What if new throws? Who owns driver?

In a typical 100K-line C++ codebase you might find hundreds of dynamic_cast calls (each a potential runtime failure), hundreds of raw new calls (each a potential leak), and hundreds of virtual/override methods (vtable overhead everywhere).

Dangling references and lambda captures

int& get_reference() {
    int x = 42;
    return x;  // Dangling reference — compiles, UB at runtime
}

auto make_closure() {
    int local = 42;
    return [&local]() { return local; };  // Dangling capture!
}

The visualization: C++ additional problems

graph TD
    ROOT["C++ Additional Problems<br/>(on top of C issues)"] --> UAM["Use-After-Move"]
    ROOT --> CYCLE["Reference Cycles"]
    ROOT --> ITER["Iterator Invalidation"]
    ROOT --> EXC["Exception Safety"]
    ROOT --> TMPL["Template Error Messages"]

    UAM --> UAM1["std::move leaves zombie<br/>Compiles without warning"]
    CYCLE --> CYCLE1["shared_ptr cycles leak<br/>Destructor never called"]
    ITER --> ITER1["erase() invalidates iterators<br/>Real production bugs"]
    EXC --> EXC1["Partial construction<br/>new without try/catch"]
    TMPL --> TMPL1["30+ lines of nested<br/>template instantiation errors"]

    style ROOT fill:#ff6b6b,color:#000
    style UAM fill:#ffa07a,color:#000
    style CYCLE fill:#ffa07a,color:#000
    style ITER fill:#ffa07a,color:#000
    style EXC fill:#ffa07a,color:#000
    style TMPL fill:#ffa07a,color:#000

How Rust Addresses All of This / Rust 如何解决这一切

Every problem listed above — from both C and C++ — is prevented by Rust’s compile-time guarantees: 上面列出的所有问题 —— 无论来自 C 还是 C++ —— 都能被 Rust 的编译期保证所预防:

Problem / 问题Rust’s Solution / Rust 的解法
Buffer overflows / 缓冲区溢出Slices carry length; indexing is bounds-checked / 切片自带长度;索引会自动检查边界
Dangling pointers / use-after-free / 悬垂指针 / 释放后使用Lifetime system proves references are valid at compile time / 生命周期系统在编译期证明引用的有效性
Use-after-move / move 后继续使用Moves are destructive — compiler refuses to let you touch the original / 移动是破坏性的 —— 编译器拒绝让你接触原始对象
Memory leaks / 内存泄漏Drop trait = RAII without the Rule of Five; automatic, correct cleanup / Drop trait = 无需 Rule of Five 的 RAII;自动、正确的清理
Reference cycles / 引用环Ownership is tree-shaped; Rc + Weak makes cycles explicit / 所有权是树状的;Rc + Weak 让环变得显式
Iterator invalidation / 迭代器失效Borrow checker forbids mutating a collection while borrowing it / 借用检查器禁止在借用集合的同时对其进行修改
NULL pointers / 空指针No null. Option<T> forces explicit handling via pattern matching / 不存在 null。Option<T> 通过模式匹配强制显式处理
Data races / 数据竞争Send/Sync traits make data races a compile error / Send/Sync trait 让数据竞争成为编译错误
Uninitialized variables / 未初始化变量All variables must be initialized; compiler enforces it / 所有变量必须经过初始化;由编译器强制执行
Integer UB / 整数未定义行为Debug panics on overflow; release wraps (both defined behavior) / 调试编译在溢出时 panic;发布编译回绕(两者皆为定义行为)
Exceptions / 异常No exceptions; Result<T, E> is visible in type signatures, propagated with ? / 无异常;Result<T, E> 在类型签名中可见,通过 ? 传播
Inheritance complexity / 继承复杂性Traits + composition; no Diamond Problem, no vtable fragility / Trait + 组合;没有菱形继承问题,没有 vtable 的脆弱性
Forgotten mutex unlocks / 忘记互斥锁解锁Mutex<T> wraps the data; lock guard is the only access path / Mutex<T> 包装了数据;锁保护对象是唯一的访问路径
#![allow(unused)]
fn main() {
fn rust_prevents_everything() {
    // ✅ No buffer overflow — bounds checked / 没有缓冲区溢出 —— 检查了边界
    let arr = [1, 2, 3, 4, 5];
    // arr[10];  // panic at runtime, never UB / 运行时 panic,绝非未定义行为

    // ✅ No use-after-move — compile error / 没有 move 后使用 —— 编译错误
    let data = vec![1, 2, 3];
    let moved = data;
    // data.len();  // error: value used after move / 错误:move 后使用了值

    // ✅ No dangling pointer — lifetime error / 没有悬垂指针 —— 生命周期错误
    // let r;
    // { let x = 5; r = &x; }  // error: x does not live long enough / 错误:x 的存活时间不够长

    // ✅ No null — Option forces handling / 没有空值 —— Option 强制处理
    let maybe: Option<i32> = None;
    // maybe.unwrap();  // panic, but you'd use match or if let instead / 会 panic,但你通常应该改用 match 或 if let

    // ✅ No data race — compile error / 没有数据竞争 —— 编译错误
    // let mut shared = vec![1, 2, 3];
    // shared_push(5);                         //   borrowed value / 被借用的值
 }
}

Rust’s safety model — the full picture / Rust 安全模型 —— 全景图

graph TD
    RUST["Rust Safety Guarantees<br/>Rust 安全保证"] --> OWN["Ownership System<br/>所有权系统"]
    RUST --> BORROW["Borrow Checker<br/>借用检查器"]
    RUST --> TYPES["Type System<br/>类型系统"]
    RUST --> TRAITS["Send/Sync Traits<br/>Send/Sync Trait"]
 
    OWN --> OWN1["No use-after-free<br/>No use-after-move<br/>No double-free<br/>无释放后使用<br/>无 move 后使用<br/>无双重释放"]
    BORROW --> BORROW1["No dangling references<br/>No iterator invalidation<br/>No data races through refs<br/>无悬垂引用<br/>无迭代器失效<br/>无通过引用的数据竞争"]
    TYPES --> TYPES1["No NULL (Option&lt;T&gt;)<br/>No exceptions (Result&lt;T,E&gt;)<br/>No uninitialized values<br/>无 NULL (Option&lt;T&gt;)<br/>无异常 (Result&lt;T,E&gt;)<br/>无未初始化值"]
    TRAITS --> TRAITS1["No data races<br/>Send = safe to transfer<br/>Sync = safe to share<br/>无数据竞争<br/>Send = 跨线程所有权转移安全<br/>Sync = 跨线程共享安全"]
 
     style RUST fill:#51cf66,color:#000
     style OWN fill:#91e5a3,color:#000
     style BORROW fill:#91e5a3,color:#000
     style TYPES fill:#91e5a3,color:#000
     style TRAITS fill:#91e5a3,color:#000

-| Concept | C | C++ | Rust | Key Difference | +| Concept / 概念 | C | C++ | Rust | Key Difference / 关键差异 | |———––|—––|———|–––––|—————––| -| Memory management | malloc()/free() | unique_ptr, shared_ptr | Box<T>, Rc<T>, Arc<T> | Automatic, no cycles, no zombies | +| Memory management / 内存管理 | malloc()/free() | unique_ptr, shared_ptr | Box<T>, Rc<T>, Arc<T> | Automatic, no cycles, no zombies / 自动、无环、无僵尸对象 | -| Arrays | int arr[10] | std::vector<T>, std::array<T> | Vec<T>, [T; N] | Bounds checking by default | +| Arrays / 数组 | int arr[10] | std::vector<T>, std::array<T> | Vec<T>, [T; N] | Bounds checking by default / 默认进行边界检查 | -| Strings | char* with \0 | std::string, string_view | String, &str | UTF-8 guaranteed, lifetime-checked | +| Strings / 字符串 | char* with \0 | std::string, string_view | String, &str | UTF-8 guaranteed, lifetime-checked / 保证 UTF-8,且强制生命周期检查 | -| References | int* (raw) | T&, T&& (move) | &T, &mut T | Lifetime + borrow checking | +| References / 引用 | int* (raw) | T&, T&& (move) | &T, &mut T | Lifetime + borrow checking / 生命周期 + 借用检查 | -| Polymorphism | Function pointers | Virtual functions, inheritance | Traits, trait objects | Composition over inheritance | +| Polymorphism / 多态 | Function pointers / 函数指针 | Virtual functions, inheritance / 虚函数、继承 | Traits, trait objects / Trait 与 Trait 对象 | Composition over inheritance / 组合优于继承 | -| Generics | Macros / void* | Templates | Generics + trait bounds | Clear error messages | +| Generics / 泛型 | Macros / void* | Templates | Generics + trait bounds / 泛型 + Trait 约束 | Clear error messages / 错误信息更清晰 | -| Error handling | Return codes, errno | Exceptions, std::optional | Result<T, E>, Option<T> | No hidden control flow | +| Error handling / 错误处理 | Return codes, errno / 返回码、errno | Exceptions, std::optional / 异常、std::optional | Result<T, E>, Option<T> | No hidden control flow / 没有隐藏的控制流 | -| NULL safety | ptr == NULL | nullptr, std::optional<T> | Option<T> | Forced null checking | +| NULL safety / 空值安全 | ptr == NULL | nullptr, std::optional<T> | Option<T> | Forced null checking / 强制空值检查 | -| Thread safety | Manual (pthreads) | Manual (std::mutex, etc.) | Compile-time Send/Sync | Data races impossible | +| Thread safety / 线程安全 | Manual (pthreads) / 手动 | Manual (std::mutex, etc.) / 手动同步 | Compile-time Send/Sync / 编译期 Send/Sync | Data races impossible / 数据竞争在结构上是不可能的 | -| Build system | Make, CMake | CMake, Make, etc. | Cargo | Integrated toolchain | +| Build system / 构建系统 | Make, CMake | CMake, Make, etc. | Cargo | Integrated toolchain / 集成化的工具链 | -| Undefined behavior | Rampant | Subtle (signed overflow, aliasing) | Zero in safe code | Safety guaranteed | +| Undefined behavior / 未定义行为 | Rampant / 随处可见 | Subtle (signed overflow, aliasing) / 隐蔽(如有符号溢出、别名) | Zero in safe code / 安全代码中为零 | Safety guaranteed / 安全性更有保障 |


2. Enough talk already: Show me some code / 2. 少说多练:先看代码

What you’ll learn / 你将学到: Your first Rust program — fn main(), println!(), and how Rust macros differ fundamentally from C/C++ preprocessor macros. By the end you’ll be able to write, compile, and run simple Rust programs.

你的第一个 Rust 程序 —— fn main()println!() 以及 Rust 宏如何从根本上区别于 C/C++ 的预处理器宏。学完本章,你将能够编写、编译并运行简单的 Rust 程序。

fn main() {
    println!("Hello world from Rust");
}
  • The above syntax should be similar to anyone familiar with C-style languages / 以上语法对于熟悉 C 风格语言的人来说应该很亲切
    • All functions in Rust begin with the fn keyword / Rust 中所有函数都以 fn 关键字开头
    • The default entry point for executables is main() / 可执行程序的默认入口点是 main()
    • The println! looks like a function, but is actually a macro. Macros in Rust are very different from C/C++ preprocessor macros — they are hygienic, type-safe, and operate on the syntax tree rather than text substitution / println! 看起来像函数,但实际上是一个。Rust 中的宏与 C/C++ 的预处理器宏非常不同 —— 它们是卫生的、类型安全的,并且是在语法树上操作而非简单的文本替换
  • Two great ways to quickly try out Rust snippets / 两种快速尝试 Rust 代码片段的好主意:
    • Online / 在线: Rust Playground — paste code, hit Run, share results. No install needed / 粘贴代码、点击运行、分享结果。无需安装
    • Local REPL / 本地 REPL: Install evcxr_repl for an interactive Rust REPL (like Python’s REPL, but for Rust) / 安装 evcxr_repl 以获得交互式 Rust REPL(像 Python 的 REPL,但用于 Rust):
cargo install --locked evcxr_repl
evcxr   # Start the REPL, type Rust expressions interactively / 启动 REPL,交互式键入 Rust 表达式

Rust Local installation / Rust 本地安装

  • Rust can be locally installed using the following methods / 可以使用以下方法在本地安装 Rust:
    • Windows:https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe
    • Linux / WSL:curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
  • The Rust ecosystem is composed of the following components / Rust 生态由以下组件构成:
    • rustc 是独立的编译器,但很少直接使用
    • 首选工具 cargo 是万能军刀,用于依赖管理、构建、测试、格式化、代码扫描(linting)等
    • Rust 工具链有 stable(稳定版)、beta(测试版)和 nightly(开发版/实验性)三个频道,但我们会坚持使用 stable。每六周发布一次新版本,使用 rustup update 命令升级 stable 安装
  • We’ll also install the rust-analyzer plug-in for VSCode / 我们还将为 VSCode 安装 rust-analyzer 插件

Rust packages (crates) / Rust 包(crate)

  • Rust binaries are created using packages (hereby called crates) / Rust 二进制文件是使用包(此处称为 crate)创建的
    • 一个 crate 既可以是独立的,也可以依赖于其他 crate。依赖的 crate 可以是本地的或远程的。第三方 crate 通常从名为 crates.io 的集中仓库下载。
    • cargo 工具会自动处理 crate 及其依赖项的下载。这在概念上相当于链接 C 库
    • Crate 依赖项在一个名为 Cargo.toml 的文件中表达。它还定义了 crate 的目标类型:独立可执行文件、静态库、动态库(少见)
    • 参考:https://doc.rust-lang.org/cargo/reference/cargo-targets.html

Cargo vs Traditional C Build Systems

Dependency Management Comparison

graph TD
    subgraph "Traditional C Build Process"
        CC["C Source Files<br/>(.c, .h)"]
        CM["Manual Makefile<br/>or CMake"]
        CL["Linker"]
        CB["Final Binary"]
        
        CC --> CM
        CM --> CL
        CL --> CB
        
        CDep["Manual dependency<br/>management"]
        CLib1["libcurl-dev<br/>(apt install)"]
        CLib2["libjson-dev<br/>(apt install)"]
        CInc["Manual include paths<br/>-I/usr/include/curl"]
        CLink["Manual linking<br/>-lcurl -ljson"]
        
        CDep --> CLib1
        CDep --> CLib2
        CLib1 --> CInc
        CLib2 --> CInc
        CInc --> CM
        CLink --> CL
        
        C_ISSUES["[ERROR] Version conflicts<br/>[ERROR] Platform differences<br/>[ERROR] Missing dependencies<br/>[ERROR] Linking order matters<br/>[ERROR] No automated updates"]
    end
    
    subgraph "Rust Cargo Build Process"
        RS["Rust Source Files<br/>(.rs)"]
        CT["Cargo.toml<br/>[dependencies]<br/>reqwest = '0.11'<br/>serde_json = '1.0'"]
        CRG["Cargo Build System"]
        RB["Final Binary"]
        
        RS --> CRG
        CT --> CRG
        CRG --> RB
        
        CRATES["crates.io<br/>(Package registry)"]
        DEPS["Automatic dependency<br/>resolution"]
        LOCK["Cargo.lock<br/>(Version pinning)"]
        
        CRATES --> DEPS
        DEPS --> CRG
        CRG --> LOCK
        
        R_BENEFITS["[OK] Semantic versioning<br/>[OK] Automatic downloads<br/>[OK] Cross-platform<br/>[OK] Transitive dependencies<br/>[OK] Reproducible builds"]
    end
    
    style C_ISSUES fill:#ff6b6b,color:#000
    style R_BENEFITS fill:#91e5a3,color:#000
    style CM fill:#ffa07a,color:#000
    style CDep fill:#ffa07a,color:#000
    style CT fill:#91e5a3,color:#000
    style CRG fill:#91e5a3,color:#000
    style DEPS fill:#91e5a3,color:#000
    style CRATES fill:#91e5a3,color:#000

Cargo Project Structure / Cargo 项目结构

my_project/
|-- Cargo.toml          # Project configuration (like package.json) / 项目配置
|-- Cargo.lock          # Exact dependency versions (auto-generated) / 准确的依赖版本(自动生成)
|-- src/
|   |-- main.rs         # Main entry point for binary / 二进制程序主入口
|   |-- lib.rs          # Library root (if creating a library) / 库入口
|   `-- bin/            # Additional binary targets / 额外的二进制目标
|-- tests/              # Integration tests / 集成测试
|-- examples/           # Example code / 示例代码
|-- benches/            # Benchmarks / 基准测试
`-- target/             # Build artifacts (like C's build/ or obj/) / 构建产物(相当于 C 的 build/ 或 obj/)
    |-- debug/          # Debug builds (fast compile, slow runtime) / 调试构建(编译快,运行快)
    `-- release/        # Release builds (slow compile, fast runtime) / 发布构建(编译慢,运行快)

Common Cargo Commands / 常用 Cargo 命令

graph LR
    subgraph "Project Lifecycle / 项目生命周期"
        NEW["cargo new my_project<br/>创建新项目"]
        CHECK["cargo check<br/>快速语法检查"]
        BUILD["cargo build<br/>编译项目"]
        RUN["cargo run<br/>构建并执行"]
        TEST["cargo test<br/>运行所有测试"]
         
         NEW --> CHECK
         CHECK --> BUILD
         BUILD --> RUN
         BUILD --> TEST
     end
     
    subgraph "Advanced Commands / 进阶命令"
        UPDATE["cargo update<br/>更新依赖"]
        FORMAT["cargo fmt<br/>格式化代码"]
        LINT["cargo clippy<br/>Lint 检查与建议"]
        DOC["cargo doc<br/>生成文档"]
        PUBLISH["cargo publish<br/>发布到 crates.io"]
    end
    
    subgraph "Build Profiles / 构建配置"
        DEBUG["cargo build<br/>调试模式<br/>编译快,运行慢<br/>带调试符号"]
        RELEASE["cargo build --release<br/>发布模式<br/>编译慢,运行快<br/>全量优化"]
    end
    
    style NEW fill:#a3d5ff,color:#000
    style CHECK fill:#91e5a3,color:#000
    style BUILD fill:#ffa07a,color:#000
    style RUN fill:#ffcc5c,color:#000
    style TEST fill:#c084fc,color:#000
    style DEBUG fill:#94a3b8,color:#000
    style RELEASE fill:#ef4444,color:#000

Example: cargo and crates / 示例:cargo 与 crate

  • In this example, we have a standalone executable crate with no other dependencies / 在此示例中,我们只有一个没有其他依赖项的独立可执行 crate
  • Use the following commands to create a new crate called helloworld / 使用以下命令创建一个名为 helloworld 的新 crate
cargo new helloworld
cd helloworld
cat Cargo.toml

+— + +In this book we’ll primarily be using cargo run and cargo test. + +在本书中,我们主要会使用 cargo runcargo test。 + +Now that we hopefully have an environment setup and are ready to compile, let’s jump straight in and look at some Rust language basics. + +现在我们已经搭建好了环境并准备好进行编译,让我们直接进入 Rust 语言的基础知识学习。 + +Next: Variables and Types >> + +下一章:变量与类型 >>

3. Built-in Rust types / 3. Rust 内建类型

What you’ll learn / 你将学到: Rust’s fundamental types (i32, u64, f64, bool, char), type inference, explicit type annotations, and how they compare to C/C++ primitive types. No implicit conversions — Rust requires explicit casts.

Rust 的基本类型(i32u64f64boolchar)、类型推导、显式类型标注,以及它们与 C/C++ 原生类型的对比。Rust 没有隐式转换 —— 所有转换都必须显式使用 cast。

  • Rust has type inference, but also allows explicit specification of the type / Rust 拥有类型推导能力,但也允许显式指定类型
Description / 描述Type / 类型Example / 示例
Signed integers / 有符号整数i8, i16, i32, i64, i128, isize-1, 42, 1_00_000, 1_00_000i64
Unsigned integers / 无符号整数u8, u16, u32, u64, u128, usize0, 42, 42u32, 42u64
Floating point / 浮点数f32, f640.0, 0.42
Unicode / Unicode 字符char‘a’, ‘$’
Boolean / 布尔值booltrue, false
  • Rust permits arbitrarily use of _ between numbers for ease of reading / Rust 允许在数字之间任意使用 _ 以提高可读性

Rust type specification and assignment / Rust 类型指定与赋值

  • Rust uses the let keyword to assign values to variables. The type of the variable can be optionally specified after a : / Rust 使用 let 关键字为变量赋值。类型可以可选地写在 : 之后
fn main() {
    let x : i32 = 42;
    // These two assignments are logically equivalent / 这两个赋值在逻辑上是等价的
    let y : u32 = 42;
    let z = 42u32;
}
  • Function parameters and return values (if any) require an explicit type. The following takes an u8 parameter and returns u32 / 函数参数和返回值(如果有)需要显式指定类型。以下函数接收一个 u8 参数并返回 u32
#![allow(unused)]
fn main() {
fn foo(x : u8) -> u32
{
    return x as u32 * x as u32;
}
}
  • Unused variables are prefixed with _ to avoid compiler warnings / 未使用的变量以前缀 _ 开头,以避免编译器警告

Rust type specification and inference / Rust 类型指定与推导

fn secret_of_life_u32(x : u32) {
    println!("The u32 secret_of_life is {}", x);
}

fn secret_of_life_u8(x : u8) {
    println!("The u8 secret_of_life is {}", x);
}

fn main() {
    let a = 42; // The let keyword assigns a value; type of a is u32 / let 关键字赋值;a 的类型被推导为 u32
    let b = 42; // The let keyword assigns a value; inferred type of b is u8 / let 关键字赋值;b 的推导类型为 u8
    secret_of_life_u32(a);
    secret_of_life_u8(b);
}

Rust variables and mutability / Rust 变量与可变性

  • Rust variables are immutable by default unless the mut keyword is used to denote that a variable is mutable. For example, the following code will not compile unless the let a = 42 is changed to let mut a = 42 / Rust 变量默认是不可变的,除非使用 mut 关键字声明变量可变。例如,除非将 let a = 42 改为 let mut a = 42,否则以下代码将无法编译
fn main() {
    let a = 42; // Must be changed to let mut a = 42 to permit the assignment below / 必须改为 let mut a = 42 才能允许下面的赋值
    a = 43;  // Will not compile unless the above is changed / 除非修改上面一行,否则无法编译
}
  • Rust permits the reuse of the variable names (shadowing) / Rust 允许变量名复用(变量遮蔽,Shadowing)
fn main() {
    let a = 42;
    {
        let a = 43; //OK: Different variable with the same name / OK:同名但不同的变量
    }
    // a = 43; // Not permitted / 不允许
    let a = 43; // Ok: New variable and assignment / Ok:新变量及赋值
}

4. Control Flow / 4. 控制流

What you’ll learn / 你将学到: Rust’s control flow constructs — if/else as expressions, loop/while/for, match, and how they differ from C/C++ counterparts. The key insight: most Rust control flow returns values.

Rust 的控制流结构 —— 作为表达式的 if/elseloop/while/formatch,以及它们与 C/C++ 对应部分的区别。核心见解:大多数 Rust 控制流都会返回值。

  • In Rust, if is actually an expression, i.e., it can be used to assign values, but it also behaves like a statement. ▶ Try it / 尝试运行
fn main() {
    let x = 42;
    if x < 42 {
        println!("Smaller than the secret of life / 小于生命之秘");
    } else if x == 42 {
        println!("Is equal to the secret of life / 等于生命之秘");
    } else {
        println!("Larger than the secret of life / 大于生命之秘");
    }
    let is_secret_of_life = if x == 42 {true} else {false};
    println!("{}", is_secret_of_life);
}

Rust loops using while and for / Rust 循环:while 与 for

  • The while keyword can be used to loop while an expression is true / while 关键字用于在表达式为真时进行循环
fn main() {
    let mut x = 40;
    while x != 42 {
        x += 1;
    }
}
  • The for keyword can be used to iterate over ranges / for 关键字可用于遍历范围
fn main() {
    // Will not print 43; use 40..=43 to include last element
    // 不会打印 43;使用 40..=43 来包含最后一个元素
    for x in 40..43 {
        println!("{}", x);
    } 
}

Rust loops using loop / Rust 循环:loop

  • The loop keyword creates an infinite loop until a break is encountered / loop 关键字创建一个无限循环,直到遇到 break
fn main() {
    let mut x = 40;
    // Change the below to 'here: loop to specify optional label for the loop
    // 将下面改为 'here: loop 可以为循环指定可选标签
    loop {
        if x == 42 {
            break; // Use break x; to return the value of x / 使用 break x; 可以返回 x 的值
        }
        x += 1;
    }
}
  • The break statement can include an optional expression that can be used to assign the value of a loop expression / break 语句可以包含一个可选表达式,用于为 loop 表达式赋值
  • The continue keyword can be used to return to the top of the loop / continue 关键字可以用于返回 loop 的顶部
  • Loop labels can be used with break or continue and are useful when dealing with nested loops / 循环标签可以配合 breakcontinue 使用,在处理嵌套循环时非常有用

Rust expression blocks / Rust 表达式块

  • Rust expression blocks are simply a sequence of expressions enclosed in {}. The evaluated value is simply the last expression in the block / Rust 表达式块只是用 {} 包裹的一系列表达式。其求值结果就是块中的最后一个表达式
fn main() {
    let x = {
        let y = 40;
        y + 2 // Note: ; must be omitted / 注意:必须省略分号 ;
    };
    // Notice the Python style printing / 注意这种 Python 风格的打印方式
    println!("{x}");
}
  • Rust style is to use this to omit the return keyword in functions / Rust 的风格是利用这一点在函数中省略 return 关键字
fn is_secret_of_life(x: u32) -> bool {
    // Same as if x == 42 {true} else {false}
    // 等同于 if x == 42 {true} else {false}
    x == 42 // Note: ; must be omitted / 注意:必须省略分号 ;
}
fn main() {
    println!("{}", is_secret_of_life(42));
}

5. Data Structures / 5. 数据结构

Rust array type / Rust 数组类型

What you’ll learn / 你将学到: Rust’s core data structures — arrays, tuples, slices, strings, structs, Vec, and HashMap. This is a dense chapter; focus on understanding String vs &str and how structs work. You’ll revisit references and borrowing in depth in chapter 7.

  • Rust 的核心数据结构 —— 数组、元组、切片、字符串、结构体、VecHashMap。这是一个内容密集的章节;请重点理解 String&str 的区别以及结构体的工作原理。你将在第 7 章深入学习引用和借用。

    • Arrays contain a fixed number of elements of the same type
    • Arrays contain a fixed number of elements of the same type / 数组包含固定数量的同类型元素
  • - Like all other Rust types, arrays are immutable by default (unless mut is used)
    
  • - Like all other Rust types, arrays are immutable by default (unless mut is used) / 与所有其他 Rust 类型一样,数组默认是不可变的(除非使用了 mut)
    
  • - Arrays are indexed using [] and are bounds checked. The len() method can be used to obtain the length of the array
    
  • - Arrays are indexed using [] and are bounds checked. The len() method can be used to obtain the length of the array / 数组使用 [] 进行索引,并且会进行边界检查。可以使用 len() 方法获取数组长度
    
    fn get_index(y : usize) -> usize {
        y+1        
    }
    
    fn main() {
-        // Initializes an array of 3 elements and sets all to 42
+        // Initializes an array of 3 elements and sets all to 42 / 初始化一个 3 元素的数组,全部设为 42
        let a : [u8; 3] = [42; 3];
-        // Alternative syntax
+        // Alternative syntax / 替代语法
        // let a = [42u8, 42u8, 42u8];
        for x in a {
            println!("{x}");
        }
        let y = get_index(a.len());
-        // Commenting out the below will cause a panic
+        // Commenting out the below will cause a panic / 下面一行如果取消注释会触发 panic
        //println!("{}", a[y]);
    }

    • Arrays can be nested
    • Arrays can be nested / 数组可以嵌套
  • - Rust has several built-in formatters for printing. In the below, the ```:?``` is the ```debug``` print formatter. The ```:#?``` formatter can be used for ```pretty print```. These formatters can be customized per type (more on this later) 
    
  • - Rust has several built-in formatters for printing. In the below, the ```:?``` is the ```debug``` print formatter. The ```:#?``` formatter can be used for ```pretty print```. These formatters can be customized per type (more on this later) / Rust 有几种内置的格式化程序。在下面,```:?``` 是 ```debug``` 打印格式化程序。```:#?``` 格式化程序可用于 ```pretty print```(美化打印)。这些格式化程序可以按类型自定义(稍后会详细介绍)
    
    fn main() {
        let a = [
-            [40, 0], // Define a nested array
+            [40, 0], // Define a nested array / 定义嵌套数组
            [41, 0],
            [42, 1],
        ];
        for x in a {
            println!("{x:?}");
        }
    }

    • Tuples have a fixed size and can group arbitrary types into a single compound type
    • Tuples have a fixed size and can group arbitrary types into a single compound type / 元组具有固定大小,可以将任意类型组合成单个复合类型
  • - The constituent types can be indexed by their relative location (.0, .1, .2, ...). An empty tuple, i.e., () is called the unit value and is the equivalent of a void return value
    
  • - The constituent types can be indexed by their relative location (.0, .1, .2, ...). An empty tuple, i.e., () is called the unit value and is the equivalent of a void return value / 组成类型可以通过其相对位置(.0, .1, .2, ...)进行索引。空元组即 () 被称为 unit(单元)值,相当于 void 返回值
    
  • - Rust supports tuple destructuring to make it easy to bind variables to individual elements
    
  • - Rust supports tuple destructuring to make it easy to bind variables to individual elements / Rust 支持元组解构,可以轻松地将变量绑定到各个元素
    
fn get_tuple() -> (u32, bool) {
    (42, true)        
}

fn main() {
   let t : (u8, bool) = (42, true);
   let u : (u32, bool) = (43, false);
   println!("{}, {}", t.0, t.1);
   println!("{}, {}", u.0, u.1);
-    let (num, flag) = get_tuple(); // Tuple destructuring
+    let (num, flag) = get_tuple(); // Tuple destructuring / 元组解构
   println!("{num}, {flag}");
}
    • References in Rust are roughly equivalent to pointers in C with some key differences
    • References in Rust are roughly equivalent to pointers in C with some key differences / Rust 中的引用大致相当于 C 中的指针,但有一些关键区别
  • - It is legal to have any number of read-only (immutable) references to a variable at any point of time. A reference cannot outlive the variable scope (this is a key concept called **lifetime**; discussed in detail later)
    
  • - It is legal to have any number of read-only (immutable) references to a variable at any point of time. A reference cannot outlive the variable scope (this is a key concept called **lifetime**; discussed in detail later) / 在任何时间点拥有任意数量的只读(不可变)变量引用都是合法的。引用不能超出变量作用域的存活期(这是一个名为**生命周期**的关键概念;稍后会详细讨论)
    
  • - Only a single writable (mutable) reference to a mutable variable is permitted and it must not overlap with any other reference.
    
  • - Only a single writable (mutable) reference to a mutable variable is permitted and it must not overlap with any other reference. / 对于可变变量,只允许存在一个可写(可变)引用,且它不能与任何其他引用重叠。
    
fn main() {
    let mut a = 42;
    {
        let b = &a;
        let c = b;
-        println!("{} {}", *b, *c); // The compiler automatically dereferences *c
+        println!("{} {}", *b, *c); // The compiler automatically dereferences *c / 编译器会自动对 *c 进行解引用
-        // Illegal because b and still are still in scope
+        // Illegal because b and still are still in scope / 非法,因为 b 仍然在作用域内
        // let d = &mut a;
    }
-    let d = &mut a; // Ok: b and c are not in scope
+    let d = &mut a; // Ok: b and c are not in scope / Ok:b 和 c 不在作用域内
    *d = 43;
}

    • Rust references can be used to create subsets of arrays
    • Rust references can be used to create subsets of arrays / Rust 引用可用于创建数组的子集
  • - Unlike arrays, which have a static fixed length determined at compile time, slices can be of arbitrary size. Internally, slices are implemented as a "fat-pointer" that contains the length of the slice and a pointer to the starting element in the original array
    
  • - Unlike arrays, which have a static fixed length determined at compile time, slices can be of arbitrary size. Internally, slices are implemented as a "fat-pointer" that contains the length of the slice and a pointer to the starting element in the original array / 与编译时确定静态固定长度的数组不同,切片可以是任意大小。在内部,切片被实现为一个“胖指针(fat-pointer)”,包含切片长度和指向原始数组起始元素的指针
    
fn main() {
    let a = [40, 41, 42, 43];
-    let b = &a[1..a.len()]; // A slice starting with the second element in the original
+    let b = &a[1..a.len()]; // A slice starting with the second element in the original / 从原始数组第二个元素开始的切片
-    let c = &a[1..]; // Same as the above
+    let c = &a[1..]; // Same as the above / 同上
-    let d = &a[..]; // Same as &a[0..] or &a[0..a.len()]
+    let d = &a[..]; // Same as &a[0..] or &a[0..a.len()] / 等同于 &a[0..] 或 &a[0..a.len()]
    println!("{b:?} {c:?} {d:?}");
}

    • The const keyword can be used to define a constant value. Constant values are evaluated at compile time and are inlined into the program
    • The const keyword can be used to define a constant value. Constant values are evaluated at compile time and are inlined into the program / const 关键字用于定义常量值。常量值在编译时求值,并被内联到程序中
    • The static keyword is used to define the equivalent of global variables in languages like C/C++ Static variables have an addressable memory location and are created once and last the entire lifetime of the program
    • The static keyword is used to define the equivalent of global variables in languages like C/C++ Static variables have an addressable memory location and are created once and last the entire lifetime of the program / static 关键字用于定义相当于 C/C++ 中全局变量的变量。静态变量具有可寻址的内存位置,只创建一次,并持续整个程序生命周期
const SECRET_OF_LIFE: u32 = 42;
static GLOBAL_VARIABLE : u32 = 2;
fn main() {
    println!("The secret of life is {}", SECRET_OF_LIFE);
    println!("Value of global variable is {GLOBAL_VARIABLE}")
}

    • Rust has two string types that serve different purposes
    • Rust has two string types that serve different purposes / Rust 有两种字符串类型,用于不同的目的
  • - `String` — owned, heap-allocated, growable (like C's `malloc`'d buffer, or C++'s `std::string`)
    
  • - `String` — owned, heap-allocated, growable (like C's `malloc`'d buffer, or C++'s `std::string`) / `String` —— 有所有权的、堆分配的、可增长的(类似于 C 的 `malloc` 缓冲区或 C++ 的 `std::string`)
    
  • - `&str` — borrowed, lightweight reference (like C's `const char*` with length, or C++'s `std::string_view` — but `&str` is **lifetime-checked** so it can never dangle)
    
  • - `&str` — borrowed, lightweight reference (like C's `const char*` with length, or C++'s `std::string_view` — but `&str` is **lifetime-checked** so it can never dangle) / `&str` —— 借用的、轻量级引用(类似于 C 的带长度的 `const char*` 或 C++ 的 `std::string_view` —— 但 `&str` 是**经过生命周期检查的**,所以永远不会悬垂)
    
  • - Unlike C's null-terminated strings, Rust strings track their length and are guaranteed valid UTF-8
    
  • - Unlike C's null-terminated strings, Rust strings track their length and are guaranteed valid UTF-8 / 与 C 的以 null 结尾的字符串不同,Rust 字符串会记录长度,并保证是有效的 UTF-8
    
  • For C++ developers: Stringstd::string, &strstd::string_view. Unlike std::string_view, a &str is guaranteed valid for its entire lifetime by the borrow checker.

  • For C++ developers / C++ 开发者注意: Stringstd::string&strstd::string_view。与 std::string_view 不同,借用检查器保证 &str 在其整个生命周期内都是有效的。

-| Aspect | C char* | C++ std::string | Rust String | Rust &str | +| Aspect / 维度 | C char* | C++ std::string | Rust String | Rust &str | |————|–––––––|–––––––––––|—————––|––––––––| -| Memory | Manual (malloc/free) | Heap-allocated, owns buffer | Heap-allocated, auto-freed | Borrowed reference (lifetime-checked) | +| Memory / 内存 | Manual (malloc/free) / 手动 | Heap-allocated, owns buffer / 堆分配,拥有缓冲区 | Heap-allocated, auto-freed / 堆分配,自动释放 | Borrowed reference (lifetime-checked) / 借用引用(生命周期检查) | -| Mutability | Always mutable via pointer | Mutable | Mutable with mut | Always immutable | +| Mutability / 可变性 | Always mutable via pointer / 始终通过指针可变 | Mutable / 可变 | Mutable with mut / 使用 mut 可变 | Always immutable / 始终不可变 | -| Size info | None (relies on '\0') | Tracks length and capacity | Tracks length and capacity | Tracks length (fat pointer) | +| Size info / 长度信息 | None (relies on '\0') / 无(依赖 '\0') | Tracks length and capacity / 记录长度和容量 | Tracks length and capacity / 记录长度和容量 | Tracks length (fat pointer) / 记录长度(胖指针) | -| Encoding | Unspecified (usually ASCII) | Unspecified (usually ASCII) | Guaranteed valid UTF-8 | Guaranteed valid UTF-8 | +| Encoding / 编码 | Unspecified / 未指定 (usually ASCII) | Unspecified / 未指定 (usually ASCII) | Guaranteed valid UTF-8 / 保证有效 UTF-8 | Guaranteed valid UTF-8 / 保证有效 UTF-8 | -| Null terminator | Required | Required (c_str()) | Not used | Not used | +| Null terminator / 空结束符 | Required / 必须 | Required (c_str()) / 必须 | Not used / 不使用 | Not used / 不使用 |

fn main() {
-    // &str - string slice (borrowed, immutable, usually a string literal)
+    // &str - string slice (borrowed, immutable, usually a string literal) / 字符串切片(借用的、不可变的,通常是字面量)
    let greeting: &str = "Hello";  // Points to read-only memory

-    // String - owned, heap-allocated, growable
+    // String - owned, heap-allocated, growable / String —— 有所有权的、堆分配的、可增长的
    let mut owned = String::from(greeting);  // Copies data to heap
-    owned.push_str(", World!");        // Grow the string
+    owned.push_str(", World!");        // Grow the string / 增长字符串
-    owned.push('!');                   // Append a single character
+    owned.push('!');                   // Append a single character / 追加单个字符

-    // Converting between String and &str
+    // Converting between String and &str / 在 String 和 &str 之间转换
-    let slice: &str = &owned;          // String -> &str (free, just a borrow)
+    let slice: &str = &owned;          // String -> &str (free, just a borrow) / String -> &str(开销极低,仅借用)
-    let owned2: String = slice.to_string();  // &str -> String (allocates)
+    let owned2: String = slice.to_string();  // &str -> String (allocates) / &str -> String(会分配空间)
-    let owned3: String = String::from(slice); // Same as above
+    let owned3: String = String::from(slice); // Same as above / 同上

-    // String concatenation (note: + consumes the left operand)
+    // String concatenation (note: + consumes the left operand) / 字符串拼接(注意:+ 会消耗左操作数)
    let hello = String::from("Hello");
    let world = String::from(", World!");
-    let combined = hello + &world;  // hello is moved (consumed), world is borrowed
+    let combined = hello + &world;  // hello is moved (consumed), world is borrowed / hello 被移动(消耗),world 被借用
-    // println!("{hello}");  // Won't compile: hello was moved
+    // println!("{hello}");  // Won't compile: hello was moved / 无法编译:hello 已被移动

-    // Use format! to avoid move issues
+    // Use format! to avoid move issues / 使用 format! 避免移动问题
    let a = String::from("Hello");
    let b = String::from("World");
-    let combined = format!("{a}, {b}!");  // Neither a nor b is consumed
+    let combined = format!("{a}, {b}!");  // Neither a nor b is consumed / a 和 b 都不会被消耗

    println!("{combined}");
}
fn main() {
    let s = String::from("hello");
-    // let c = s[0];  // Won't compile! Rust strings are UTF-8, not byte arrays
+    // let c = s[0];  // Won't compile! Rust strings are UTF-8, not byte arrays / 无法编译!Rust 字符串是 UTF-8 编码,不是字节数组

-    // Safe alternatives:
+    // Safe alternatives / 安全替代方案:
-    let first_char = s.chars().next();           // Option<char>: Some('h')
+    let first_char = s.chars().next();           // Option<char>: Some('h') / 取得第一个字符
-    let as_bytes = s.as_bytes();                 // &[u8]: raw UTF-8 bytes
+    let as_bytes = s.as_bytes();                 // &[u8]: raw UTF-8 bytes / 取得原始 UTF-8 字节
-    let substring = &s[0..1];                    // &str: "h" (byte range, must be valid UTF-8 boundary)
+    let substring = &s[0..1];                    // &str: "h" (byte range, must be valid UTF-8 boundary) / 取得子串(字节范围,必须是有效的 UTF-8 边界)

    println!("First char: {:?}", first_char);
    println!("Bytes: {:?}", &as_bytes[..5]);
}
  • 🟢 Starter
  • 🟢 Starter / 入门级
    • Write a function fn count_words(text: &str) -> usize that counts the number of whitespace-separated words in a string
    • Write a function fn count_words(text: &str) -> usize that counts the number of whitespace-separated words in a string / 编写一个函数 fn count_words(text: &str) -> usize 来统计字符串中由空格分隔的单词数量
    • Write a function fn longest_word(text: &str) -> &str that returns the longest word (hint: you’ll need to think about lifetimes – why does the return type need to be &str and not String?)
    • Write a function fn longest_word(text: &str) -> &str that returns the longest word (hint: you’ll need to think about lifetimes – why does the return type need to be &str and not String?) / 编写一个函数 fn longest_word(text: &str) -> &str 来返回最长的单词(提示:你需要思考生命周期 —— 为什么返回类型必须是 &str 而不是 String?)
  • Solution (click to expand)
  • Solution (click to expand) / 解决方案(点击展开)
fn count_words(text: &str) -> usize {
    text.split_whitespace().count()
}

fn longest_word(text: &str) -> &str {
    text.split_whitespace()
        .max_by_key(|word| word.len())
        .unwrap_or("")
}

fn main() {
    let text = "the quick brown fox jumps over the lazy dog";
    println!("Word count: {}", count_words(text));       // 9
    println!("Longest word: {}", longest_word(text));     // "jumps"
}
    • The struct keyword declares a user-defined struct type
    • The struct keyword declares a user-defined struct type / struct 关键字声明用户定义的结构体类型
  • - ```struct``` members can either be named, or anonymous (tuple structs)
    
  • - ```struct``` members can either be named, or anonymous (tuple structs) / ```struct``` 成员既可以是命名的,也可以是隐名的(元组结构体)
    
    • Unlike languages like C++, there’s no notion of “data inheritance” in Rust
    • Unlike languages like C++, there’s no notion of “data inheritance” in Rust / 与 C++ 等语言不同,Rust 并没有“数据继承”的概念
fn main() {
    struct MyStruct {
        num: u32,
        is_secret_of_life: bool,
    }
    let x = MyStruct {
        num: 42,
        is_secret_of_life: true,
    };
    let y = MyStruct {
        num: x.num,
        is_secret_of_life: x.is_secret_of_life,
    };
-    let z = MyStruct { num: x.num, ..x }; // The .. means copy remaining
+    let z = MyStruct { num: x.num, ..x }; // The .. means copy remaining / .. 表示复制剩余字段
    println!("{} {} {}", x.num, y.is_secret_of_life, z.num);
}
    • Rust tuple structs are similar to tuples and individual fields don’t have names
    • Rust tuple structs are similar to tuples and individual fields don’t have names / Rust 元组结构体类似于元组,各个字段没有名称
  • - Like tuples, individual elements are accessed using .0, .1, .2, .... A common use case for tuple structs is to wrap primitive types to create custom types. **This can useful to avoid mixing differing values of the same type**
    
  • - Like tuples, individual elements are accessed using .0, .1, .2, .... A common use case for tuple structs is to wrap primitive types to create custom types. **This can useful to avoid mixing differing values of the same type** / 与元组一样,单个元素使用 .0, .1, .2, ... 访问。元组结构体的一个常见用法是包装原始类型以创建自定义类型。**这对于避免混淆相同类型的不同业务含义的值非常有用**
    
struct WeightInGrams(u32);
struct WeightInMilligrams(u32);
fn to_weight_in_grams(kilograms: u32) -> WeightInGrams {
    WeightInGrams(kilograms * 1000)
}

fn to_weight_in_milligrams(w : WeightInGrams) -> WeightInMilligrams  {
    WeightInMilligrams(w.0 * 1000)
}

fn main() {
    let x = to_weight_in_grams(42);
    let y = to_weight_in_milligrams(x);
-    // let z : WeightInGrams = x;  // Won't compile: x was moved into to_weight_in_milligrams()
+    // let z : WeightInGrams = x;  // Won't compile: x was moved / 无法编译:x 已被移动
-    // let a : WeightInGrams = y;   // Won't compile: type mismatch (WeightInMilligrams vs WeightInGrams)
+    // let a : WeightInGrams = y;   // Won't compile: type mismatch / 无法编译:类型不匹配
}
  • Note: The #[derive(...)] attribute automatically generates common trait implementations for structs and enums. You’ll see this used throughout the course:
  • Note / 注意#[derive(...)] 属性会自动为结构体和枚举生成常见的 trait 实现。你会在本课程中经常看到它的使用:
#[derive(Debug, Clone, PartialEq)]
struct Point { x: i32, y: i32 }

fn main() {
    let p = Point { x: 1, y: 2 };
-    println!("{:?}", p);           // Debug: works because of #[derive(Debug)]
+    println!("{:?}", p);           // Debug: works because of #[derive(Debug)] / Debug 打印:因 #[derive(Debug)] 而生效
-    let p2 = p.clone();           // Clone: works because of #[derive(Clone)]
+    let p2 = p.clone();           // Clone: works because of #[derive(Clone)] / 克隆:因 #[derive(Clone)] 而生效
-    assert_eq!(p, p2);            // PartialEq: works because of #[derive(PartialEq)]
+    assert_eq!(p, p2);            // PartialEq: works because of #[derive(PartialEq)] / 断言相等:因 #[derive(PartialEq)] 而生效
}
  • We’ll cover the trait system in depth later, but #[derive(Debug)] is so useful that you should add it to nearly every struct and enum you create.
  • 我们稍后会深入讲解 trait 系统,但 #[derive(Debug)] 是如此有用,以至于你几乎应该在你创建的每个 structenum 上都加上它。
    • The Vec<T> type implements a dynamic heap allocated buffer (similar to manually managed malloc/realloc arrays in C, or C++’s std::vector)
    • The Vec<T> type implements a dynamic heap allocated buffer (similar to manually managed malloc/realloc arrays in C, or C++’s std::vector) / Vec<T> 类型实现了一个动态的堆分配缓冲区(类似于 C 中手动管理的 malloc/realloc 数组,或 C++ 的 std::vector
  • - Unlike arrays with fixed size, `Vec` can grow and shrink at runtime
    
  • - Unlike arrays with fixed size, `Vec` can grow and shrink at runtime / 与固定大小的数组不同,`Vec` 可以在运行时增长和缩小
    
  • - `Vec` owns its data and automatically manages memory allocation/deallocation
    
  • - `Vec` owns its data and automatically manages memory allocation/deallocation / `Vec` 拥有其数据,并自动管理内存分配/释放
    
    • Common operations: push(), pop(), insert(), remove(), len(), capacity()
    • Common operations: push(), pop(), insert(), remove(), len(), capacity() / 常见操作:push()pop()insert()remove()len()capacity()
fn main() {
-    let mut v = Vec::new();    // Empty vector, type inferred from usage
+    let mut v = Vec::new();    // Empty vector, type inferred from usage / 空 vector,类型通过用法推导
-    v.push(42);                // Add element to end - Vec<i32>
+    v.push(42);                // Add element to end - Vec<i32> / 在末尾添加元素
    v.push(43);                
    
-    // Safe iteration (preferred)
+    // Safe iteration (preferred) / 安全迭代(推荐)
-    for x in &v {              // Borrow elements, don't consume vector
+    for x in &v {              // Borrow elements, don't consume vector / 借用元素,不消耗 vector
        println!("{x}");
    }
    
-    // Initialization shortcuts
+    // Initialization shortcuts / 初始化快捷方式
-    let mut v2 = vec![1, 2, 3, 4, 5];           // Macro for initialization
+    let mut v2 = vec![1, 2, 3, 4, 5];           // Macro for initialization / 初始化宏
-    let v3 = vec![0; 10];                       // 10 zeros
+    let v3 = vec![0; 10];                       // 10 zeros / 10 个零
    
-    // Safe access methods (preferred over indexing)
+    // Safe access methods (preferred over indexing) / 安全访问方法(优于索引)
    match v2.get(0) {
-        Some(first) => println!("First: {first}"),
+        Some(first) => println!("First: {first}"), // 取得第一个
-        None => println!("Empty vector"),
+        None => println!("Empty vector"), // vector 为空
    }
    
-    // Useful methods
+    // Useful methods / 有用的方法
    println!("Length: {}, Capacity: {}", v2.len(), v2.capacity());
-    if let Some(last) = v2.pop() {             // Remove and return last element
+    if let Some(last) = v2.pop() {             // Remove and return last element / 弹出最后一个元素
        println!("Popped: {last}");
    }
    
-    // Dangerous: direct indexing (can panic!)
+    // Dangerous: direct indexing (can panic!) / 危险:直接索引(可能导致 panic!)
    // println!("{}", v2[100]);  // Would panic at runtime
}
    • HashMap implements generic key -> value lookups (a.k.a. dictionary or map)
    • HashMap implements generic key -> value lookups (a.k.a. dictionary or map) / HashMap 实现了通用的 键(key) -> 值(value) 查找(又称 字典映射
fn main() {
-    use std::collections::HashMap;  // Need explicit import, unlike Vec
+    use std::collections::HashMap;  // Need explicit import, unlike Vec / 需要显式导入,不像 Vec 那样自动导入
-    let mut map = HashMap::new();       // Allocate an empty HashMap
+    let mut map = HashMap::new();       // Allocate an empty HashMap / 分配一个空的 HashMap
-    map.insert(40, false);  // Type is inferred as int -> bool
+    map.insert(40, false);  // Type is inferred / 类型已被推导
    map.insert(41, false);
    map.insert(42, true);
    for (key, value) in map {
        println!("{key} {value}");
    }
    let map = HashMap::from([(40, false), (41, false), (42, true)]);
    if let Some(x) = map.get(&43) {
        println!("43 was mapped to {x:?}");
    } else {
        println!("No mapping was found for 43");
    }
-    let x = map.get(&43).or(Some(&false));  // Default value if key isn't found
+    let x = map.get(&43).or(Some(&false));  // Default value / 默认值
    println!("{x:?}"); 
}
  • 🟢 Starter
  • 🟢 Starter / 入门级
    • Create a HashMap<u32, bool> with a few entries (make sure that some values are true and others are false). Loop over all elements in the hashmap and put the keys into one Vec and the values into another
    • Create a HashMap<u32, bool> with a few entries (make sure that some values are true and others are false). Loop over all elements in the hashmap and put the keys into one Vec and the values into another / 创建一个含有几个条目的 HashMap<u32, bool>(确保有些值是 true,有些是 false)。遍历 hashmap 中的所有元素,将键放入一个 Vec,将值放入另一个 Vec
  • Solution (click to expand)
  • Solution (click to expand) / 解决方案(点击展开)
use std::collections::HashMap;

fn main() {
    let map = HashMap::from([(1, true), (2, false), (3, true), (4, false)]);
    let mut keys = Vec::new();
    let mut values = Vec::new();
    for (k, v) in &map {
        keys.push(*k);
        values.push(*v);
    }
    println!("Keys:   {keys:?}");
    println!("Values: {values:?}");

-    // Alternative: use iterators with unzip()
+    // Alternative: use iterators with unzip() / 替代方案:使用带有 unzip() 的迭代器
    let (keys2, values2): (Vec<u32>, Vec<bool>) = map.into_iter().unzip();
    println!("Keys (unzip):   {keys2:?}");
    println!("Values (unzip): {values2:?}");
}

  • For C++ developers: C++ programmers often assume Rust &T works like C++ T&. While superficially similar, there are fundamental differences that cause confusion. C developers can skip this section — Rust references are covered in Ownership and Borrowing.

  • For C++ developers / C++ 开发者注意: C++ 程序员经常假设 Rust 的 &T 工作方式与 C++ 的 T& 相同。虽然表面相似,但存在导致混淆的根本差异。C 开发者可以跳过此部分 —— Rust 引用在 Ownership and Borrowing / 所有权与借用 中涵盖。

  • In C++, && has two meanings depending on context:
  • 在 C++ 中,&& 根据上下文有两种含义:
// C++: && means different things:
int&& rref = 42;           // Rvalue reference — binds to temporaries
void process(Widget&& w);   // Rvalue reference — caller must std::move

// Universal (forwarding) reference — deduced template context:
template<typename T>
void forward(T&& arg) {     // NOT an rvalue ref! Deduced as T& or T&&
    inner(std::forward<T>(arg));  // Perfect forwarding
}
  • In Rust: none of this exists. && is simply the logical AND operator.
  • 在 Rust 中:这些都不存在。 && 仅仅是逻辑与运算符。
#![allow(unused)]
fn main() {
// Rust: && is just boolean AND
let a = true && false; // false

// Rust has NO rvalue references, no universal references, no perfect forwarding.
+// Rust 没有右值引用,没有万能引用,也没有完美转发。
// Instead:
+// 相反地:
- //   - Move is the default for non-Copy types (no std::move needed)
+ //   - Move is the default for non-Copy types (no std::move needed) / 非 Copy 类型默认就是移动(不需要 std::move)
- //   - Generics + trait bounds replace universal references
+ //   - Generics + trait bounds replace universal references / 泛型 + trait 约束取代了万能引用
- //   - No temporary-binding distinction — values are values
+ //   - No temporary-binding distinction — values are values / 没有临时变量绑定的区分 —— 值就是值

- fn process(w: Widget) { }      // Takes ownership (like C++ value param + implicit move)
+ fn process(w: Widget) { }      // Takes ownership / 获取所有权
- fn process_ref(w: &Widget) { } // Borrows immutably (like C++ const T&)
+ fn process_ref(w: &Widget) { } // Borrows immutably / 不可变借用
- fn process_mut(w: &mut Widget) { } // Borrows mutably (like C++ T&, but exclusive)
+ fn process_mut(w: &mut Widget) { } // Borrows mutably / 可变借用
}

-| C++ Concept | Rust Equivalent | Notes | +| C++ Concept / C++ 概念 | Rust Equivalent / Rust 等价物 | Notes / 说明 | |———––|—————–|—––| -| T& (lvalue ref) | &T or &mut T | Rust splits into shared vs exclusive | +| T& (lvalue ref) | &T or &mut T | Rust splits into shared vs exclusive / Rust 将其拆分为共享 vs 排他 | -| T&& (rvalue ref) | Just T | Take by value = take ownership | +| T&& (rvalue ref) | Just T | Take by value = take ownership / 按值接收 = 获取所有权 | -| T&& in template (universal ref) | impl Trait or <T: Trait> | Generics replace forwarding | +| T&& in template (universal ref) | impl Trait or <T: Trait> | Generics replace forwarding / 泛型取代了转发 | -| std::move(x) | x (just use it) | Move is the default | +| std::move(x) | x (just use it) | Move is the default / 移动是默认行为 | -| std::forward<T>(x) | No equivalent needed | No universal references to forward | +| std::forward<T>(x) | No equivalent needed | No universal references to forward / 无需转发万能引用 |

  • In C++, moving is a user-defined operation (move constructor / move assignment). In Rust, moving is always a bitwise memcpy of the value, and the source is invalidated:
  • 在 C++ 中,移动是一种用户定义的处理(移动构造函数 / 移动赋值)。在 Rust 中,移动始终是值的按位 memcpy,并且源对象会失效:
#![allow(unused)]
fn main() {
// Rust move = memcpy the bytes, mark source as invalid
+// Rust move = 字节拷贝,并标记源对象无效
let s1 = String::from("hello");
- let s2 = s1; // Bytes of s1 are copied to s2's stack slot
+ let s2 = s1; // Bytes of s1 are copied to s2's stack slot / s1 的字节被拷贝到 s2 的栈槽位中
-               // s1 is now invalid — compiler enforces this
+               // s1 is now invalid — compiler enforces this / s1 现已无效 —— 编译器强制执行此规则
// println!("{s1}"); // ❌ Compile error: value used after move
}
// C++ move = call the move constructor (user-defined!)
+// C++ move = 调用移动构造函数(用户定义的!)
std::string s1 = "hello";
- std::string s2 = std::move(s1); // Calls string's move ctor
+ std::string s2 = std::move(s1); // Calls string's move ctor / 调用 string 的移动构造函数
- // s1 is now a "valid but unspecified state" zombie
+ // s1 is now a "valid but unspecified state" zombie / s1 处于“有效但未指定状态”的僵尸状态
- std::cout << s1; // Compiles! Prints... something (empty string, usually)
+ std::cout << s1; // Compiles! / 能编译!
  • Consequences:
  • 结果 / Consequences
    • Rust has no Rule of Five (no copy ctor, move ctor, copy=, move=, destructor to define)
    • Rust has no Rule of Five (no copy ctor, move ctor, copy=, move=, destructor to define) / Rust 没有 Rule of Five(不需要定义拷贝构造、移动构造、拷贝/移动赋值以及析构函数)
    • No moved-from “zombie” state — the compiler simply prevents access
    • No moved-from “zombie” state — the compiler simply prevents access / 没有 move 后的“僵尸”状态 —— 编译器直接阻止访问
    • No noexcept considerations for moves — bitwise copy can’t throw
    • No noexcept considerations for moves — bitwise copy can’t throw / 移动时无需考虑 noexcept —— 按位拷贝不会抛出异常
  • Rust automatically dereferences through multiple layers of pointers/wrappers via the Deref trait. This has no C++ equivalent:
  • Rust 通过 Deref trait 自动对多层指针/包装器进行解引用。这在 C++ 中没有等价物:
#![allow(unused)]
fn main() {
use std::sync::{Arc, Mutex};

// Nested wrapping: Arc<Mutex<Vec<String>>>
let data = Arc::new(Mutex::new(vec!["hello".to_string()]));

- // In C++, you'd need explicit unlocking and manual dereferencing at each layer.
+ // In C++, you'd need explicit unlocking and manual dereferencing at each layer. / 在 C++ 中,你需要在每一层手动解锁和手动解引用。
- // In Rust, the compiler auto-derefs through Arc → Mutex → MutexGuard → Vec:
+ // In Rust, the compiler auto-derefs through Arc → Mutex → MutexGuard → Vec: / 在 Rust 中,编译器自动通过 Arc -> Mutex -> MutexGuard -> Vec 进行解引用:
- let guard = data.lock().unwrap(); // Arc auto-derefs to Mutex
+ let guard = data.lock().unwrap(); // Arc auto-derefs / Arc 自动解引用
- let first: &str = &guard[0];      // MutexGuard→Vec (Deref), Vec[0] (Index),
+ let first: &str = &guard[0];      // MutexGuard→Vec (Deref), Vec[0] (Index)
-                                    // &String→&str (Deref coercion)
+                                    // &String→&str (Deref coercion) / &String -> &str(Deref 强制转换)
println!("First: {first}");

- // Method calls also auto-deref:
+ // Method calls also auto-deref: / 方法调用也会自动解引用:
let boxed_string = Box::new(String::from("hello"));
- println!("Length: {}", boxed_string.len());  // Box→String, then String::len()
+ println!("Length: {}", boxed_string.len());  // Box→String / Box -> String
- // No need for (*boxed_string).len() or boxed_string->len()
+ // No need for (*boxed_string).len() or boxed_string->len() / 无需使用 (*boxed_string).len() 或 boxed_string->len()
}
  • Deref coercion also applies to function arguments — the compiler inserts dereferences to make types match:
  • Deref coercion(Deref 强制转换) 也适用于函数参数 —— 编译器插入解引用操作以使类型匹配:
fn greet(name: &str) {
    println!("Hello, {name}");
}

fn main() {
    let owned = String::from("Alice");
    let boxed = Box::new(String::from("Bob"));
    let arced = std::sync::Arc::new(String::from("Carol"));

-    greet(&owned);  // &String → &str  (1 deref coercion)
+    greet(&owned);  // &String → &str / &String -> &str
-    greet(&boxed);  // &Box<String> → &String → &str  (2 deref coercions)
+    greet(&boxed);  // &Box<String> → &String → &str / 两次强制转换
-    greet(&arced);  // &Arc<String> → &String → &str  (2 deref coercions)
+    greet(&arced);  // &Arc<String> → &String → &str / 两次强制转换
-    greet("Dave");  // &str already — no coercion needed
+    greet("Dave");  // &str already / 已经是 &str
}
- // In C++ you'd need .c_str() or explicit conversions for each case.
+ // In C++ you'd need .c_str() or explicit conversions for each case. / 在 C++ 中,你需要在每种情况下使用 .c_str() 或进行显式转换。
  • The Deref chain: When you call x.method(), Rust’s method resolution
  • The Deref chain / Deref 链:当你调用 x.method() 时,Rust 的方法解析会依次尝试接收者类型 T、然后是 &T、接着是 &mut T。如果都不匹配,它会通过 Deref trait 进行解引用,并对目标类型重复此过程。
  • tries the receiver type T, then &T, then &mut T. If no match, it
  • dereferences via the Deref trait and repeats with the target type.
  • This continues through multiple layers — which is why Box<Vec<T>>
  • “just works” like a Vec<T>. Deref coercion (for function arguments)
  • is a separate but related mechanism that automatically converts &Box<String>
  • to &str by chaining Deref impls.
  • 这可以持续多个层级 —— 这就是为什么 Box<Vec<T>> 能够像 Vec<T> 一样“直接使用”。Deref coercion(用于函数参数)是一个独立但相关的机制,它通过链接 Deref 实现自动将 &Box<String> 转换为 &str
// C++: references can't be null, but pointers can, and the distinction is blurry
+// C++:引用不能为 null,但指针可以,且两者的区别很模糊
Widget& ref = *ptr;  // If ptr is null → UB
- Widget* opt = nullptr;  // "optional" reference via pointer
+ Widget* opt = nullptr;  // "optional" reference / “可选”引用
#![allow(unused)]
fn main() {
// Rust: references are ALWAYS valid — guaranteed by the borrow checker
+// Rust:引用始终有效 —— 由借用检查器保证
// No way to create a null or dangling reference in safe code
+// 在安全代码中无法创建 null 或悬垂引用
let r: &i32 = &42; // Always valid

- // "Optional reference" is explicit:
+ // "Optional reference" is explicit / “可选引用”是显式的:
- let opt: Option<&Widget> = None; // Clear intent, no null pointer
+ let opt: Option<&Widget> = None; // Clear intent / 意图很明确
if let Some(w) = opt {
-    w.do_something(); // Only reachable when present
+    w.do_something(); // Only reachable when present / 仅在存在时才可到达
}
}
// C++: a reference is an alias — it can't be rebound
+// C++:引用是一个别名 —— 无法重绑定
int a = 1, b = 2;
int& r = a;
- r = b;  // This ASSIGNS b's value to a — it does NOT rebind r!
+ r = b;  // This ASSIGNS b's value to a / 这是将 b 的值赋给 a —— 它并没有重绑定 r!
- // a is now 2, r still refers to a
+ // a is now 2, r still refers to a / a 现在是 2,r 仍然引用 a
#![allow(unused)]
fn main() {
// Rust: let bindings can shadow, but references follow different rules
+// Rust:let 绑定可以遮蔽,但引用遵循不同的规则
let a = 1;
let b = 2;
let r = &a;
- // r = &b;   // ❌ Cannot assign to immutable variable
+ // r = &b;   // ❌ Cannot assign to immutable variable / ❌ 无法为不可变变量赋值
- let r = &b;  // ✅ But you can SHADOW r with a new binding
+ let r = &b;  // ✅ But you can SHADOW r / ✅ 但你可以遮蔽 r
-              // The old binding is gone, not reseated
+              // The old binding is gone / 旧的绑定消失了,而不是重绑定了

- // With mut:
+ // With mut / 使用 mut:
let mut r = &a;
- r = &b;      // ✅ r now points to b — this IS rebinding (not assignment through)
+ r = &b;      // ✅ r now points to b / ✅ r 现在指向 b —— 这就是重绑定
}
  • Mental model: In C++, a reference is a permanent alias for one object.

  • Mental model / 心理模型:在 C++ 中,引用是某个对象的永久别名。

  • In Rust, a reference is a value (a pointer with lifetime guarantees) that

  • follows normal variable binding rules — immutable by default, rebindable

  • only if declared mut.

  • 在 Rust 中,引用是一个值(带有生命周期保证的指针),它遵循常规的变量绑定规则 —— 默认不可变,只有声明为 mut 才能重绑定。

6. Rust enum types / 6. Rust 枚举类型

What you’ll learn / 你将学到: Rust enums as discriminated unions (tagged unions done right), match for exhaustive pattern matching, and how enums replace C++ class hierarchies and C tagged unions with compiler-enforced safety.

Rust 枚举作为判别式联合(正确的标签联合实现)、用于详尽模式匹配的 match,以及枚举如何通过编译器强制的安全机制取代 C++ 类层次结构和 C 标签联合。

  • Enum types are discriminated unions, i.e., they are a sum type of several possible different types with a tag that identifies the specific variant / 枚举类型是判别式联合(discriminated unions),即它们是几种可能不同类型的和类型(sum type),带有一个标识特定变体的标签
  • - For C developers: enums in Rust can carry data (tagged unions done right — the compiler tracks which variant is active)
    
  • - For C developers / C 开发者注意:Rust 中的枚举可以携带数据(正确的标签联合实现 —— 编译器会跟踪哪个变体处于活动状态)
    
  • - For C++ developers: Rust enums are like `std::variant` but with exhaustive pattern matching, no `std::get` exceptions, and no `std::visit` boilerplate
    
  • - For C++ developers / C++ 开发者注意:Rust 枚举类似于 `std::variant`,但具有详尽的模式匹配,没有 `std::get` 异常,也没有 `std::visit` 样板代码
    
  • - The size of the `enum` is that of the largest possible type. The individual variants are not related to one another and can have completely different types
    
  • - The size of the `enum` is that of the largest possible type. The individual variants are not related to one another and can have completely different types / `enum` 的大小取决于最大可能类型的大小。各个变体之间没有关系,可以具有完全不同的类型
    
  • - `enum` types are one of the most powerful features of the language — they replace entire class hierarchies in C++ (more on this in the Case Studies)
    
  • - `enum` types are one of the most powerful features of the language — they replace entire class hierarchies in C++ (more on this in the Case Studies) / `enum` 类型是该语言最强大的特性之一 —— 它们取代了 C++ 中的整个类层次结构(稍后在案例研究中会有更多介绍)
    
fn main() {
    enum Numbers {
        Zero,
        SmallNumber(u8),
        BiggerNumber(u32),
        EvenBiggerNumber(u64),
    }
-    let a = Numbers::Zero;
+    let a = Numbers::Zero; // 零
-    let b = Numbers::SmallNumber(42);
+    let b = Numbers::SmallNumber(42); // 小数
-    let c : Numbers = a; // Ok -- the type of a is Numbers
+    let c : Numbers = a; // Ok -- the type of a is Numbers / OK —— a 的类型是 Numbers
-    let d : Numbers = b; // Ok -- the type of b is Numbers
+    let d : Numbers = b; // Ok -- the type of b is Numbers / OK —— b 的类型是 Numbers
}

    • The Rust match is the equivalent of the C “switch” on steroids
    • The Rust match is the equivalent of the C “switch” on steroids / Rust 的 match 相当于加强版的 C “switch”
  • - ```match``` can be used for pattern matching on simple data types, ```struct```, ```enum```
    
  • - ```match``` can be used for pattern matching on simple data types, ```struct```, ```enum``` / ```match``` 可用于对简单数据类型、```struct```、```enum``` 进行模式匹配
    
  • - The ```match``` statement must be exhaustive, i.e., they must cover all possible cases for a given ```type```. The ```_``` can be used a wildcard for the "all else" case
    
  • - The ```match``` statement must be exhaustive, i.e., they must cover all possible cases for a given ```type```. The ```_``` can be used a wildcard for the "all else" case / ```match``` 语句必须是详尽的,即它们必须涵盖给定类型的所有可能情况。```_``` 可用作“所有其他情况”的通配符
    
  • - ```match``` can yield a value, but all arms (```=>```) of must return a value of the same type
    
  • - ```match``` can yield a value, but all arms (```=>```) of must return a value of the same type / ```match``` 可以产生一个值,但所有分支(```=>```)必须返回相同类型的值
    
fn main() {
    let x = 42;
-    // In this case, the _ covers all numbers except the ones explicitly listed
+    // In this case, the _ covers all numbers except the ones explicitly listed / 在这种情况下,_ 涵盖了除明确列出的数字之外的所有数字
    let is_secret_of_life = match x {
-        42 => true, // return type is boolean value
+        42 => true, // return type is boolean value / 返回类型是布尔值
-        _ => false, // return type boolean value
+        _ => false, // return type boolean value / 返回类型布尔值
-        // This won't compile because return type isn't boolean
+        // This won't compile because return type isn't boolean / 这将无法编译,因为返回类型不是布尔值
        // _ => 0  
    };
    println!("{is_secret_of_life}");
}
    • match supports ranges, boolean filters, and if guard statements
    • match supports ranges, boolean filters, and if guard statements / match 支持范围、布尔过滤器和 if 守卫语句
fn main() {
    let x = 42;
    match x {
-        // Note that the =41 ensures the inclusive range
+        // Note that the =41 ensures the inclusive range / 注意 =41 确保了包含边界的范围
        0..=41 => println!("Less than the secret of life"),
        42 => println!("Secret of life"),
        _ => println!("More than the secret of life"),
    }
    let y = 100;
    match y {
-        100 if x == 43 => println!("y is 100% not secret of life"),
+        100 if x == 43 => println!("y is 100% not secret of life"), // 100% 不是生命之秘
-        100 if x == 42 => println!("y is 100% secret of life"),
+        100 if x == 42 => println!("y is 100% secret of life"), // 100% 是生命之秘
-        _ => (),    // Do nothing
+        _ => (),    // Do nothing / 什么也不做
    }
}
    • match and enums are often combined together
    • match and enums are often combined together / matchenum 经常结合在一起使用
  • - The match statement can "bind" the contained value to a variable. Use ```_``` if the value is a don't care
    
  • - The match statement can "bind" the contained value to a variable. Use ```_``` if the value is a don't care / match 语句可以将包含的值“绑定”到变量。如果不关心该值,请使用 ```_```
    
  • - The ```matches!``` macro can be used to match to specific variant
    
  • - The ```matches!``` macro can be used to match to specific variant / ```matches!``` 宏可用于匹配特定的变体
    
fn main() {
    enum Numbers {
        Zero,
        SmallNumber(u8),
        BiggerNumber(u32),
        EvenBiggerNumber(u64),
    }
    let b = Numbers::SmallNumber(42);
    match b {
        Numbers::Zero => println!("Zero"),
        Numbers::SmallNumber(value) => println!("Small number {value}"),
-        Numbers::BiggerNumber(_) | Numbers::EvenBiggerNumber(_) => println!("Some BiggerNumber or EvenBiggerNumber"),
+        Numbers::BiggerNumber(_) | Numbers::EvenBiggerNumber(_) => println!("Some BiggerNumber or EvenBiggerNumber"), // 较大的数
    }
    
-    // Boolean test for specific variants
+    // Boolean test for specific variants / 针对特定变体的布尔测试
    if matches!(b, Numbers::Zero | Numbers::SmallNumber(_)) {
        println!("Matched Zero or small number");
    }
}
    • match can also perform matches using destructuring and slices
    • match can also perform matches using destructuring and slices / match 还可以使用解构和切片进行匹配
fn main() {
    struct Foo {
        x: (u32, bool),
        y: u32
    }
    let f = Foo {x: (42, true), y: 100};
    match f {
-        // Capture the value of x into a variable called tuple
+        // Capture the value of x into a variable called tuple / 将 x 的值捕获到名为 tuple 的变量中
        Foo{y: 100, x : tuple} => println!("Matched x: {tuple:?}"),
        _ => ()
    }
    let a = [40, 41, 42];
    match a {
-        // Last element of slice must be 42. @ is used to bind the match
+        // Last element of slice must be 42. @ is used to bind the match / 切片的最后一个元素必须是 42。使用 @ 绑定匹配项
        [rest @ .., 42] => println!("{rest:?}"),
-        // First element of the slice must be 42. @ is used to bind the match
+        // First element of the slice must be 42. @ is used to bind the match / 切片的第一个元素必须是 42。使用 @ 绑定匹配项
        [42, rest @ ..] => println!("{rest:?}"),
        _ => (),
    }
}
  • 🟢 Starter
  • 🟢 Starter / 入门级
    • Write a function that implements arithmetic operations on unsigned 64-bit numbers
    • Write a function that implements arithmetic operations on unsigned 64-bit numbers / 编写一个对 64 位无符号整数执行算术运算的函数
    • Step 1: Define an enum for operations:
    • Step 1: Define an enum for operations / 第一步:定义操作枚举:
#![allow(unused)]
fn main() {
enum Operation {
    Add(u64, u64),
    Subtract(u64, u64),
}
}
    • Step 2: Define a result enum:
    • Step 2: Define a result enum / 第二步:定义结果枚举:
#![allow(unused)]
fn main() {
enum CalcResult {
-    Ok(u64),                    // Successful result
+    Ok(u64),                    // Successful result / 成功结果
-    Invalid(String),            // Error message for invalid operations
+    Invalid(String),            // Error message for invalid operations / 无效操作的错误消息
}
}
    • Step 3: Implement calculate(op: Operation) -> CalcResult
    • Step 3: Implement calculate(op: Operation) -> CalcResult / 第三步:实现 calculate(op: Operation) -> CalcResult
  • - For Add: return Ok(sum)
    
  • - For Add: return Ok(sum) / 对于加法:返回 Ok(sum)
    
  • - For Subtract: return Ok(difference) if first >= second, otherwise Invalid("Underflow")
    
  • - For Subtract: return Ok(difference) if first >= second, otherwise Invalid("Underflow") / 对于减法:如果第一个数 >= 第二个数则返回 Ok(difference),否则返回 Invalid("Underflow")
    
    • Hint: Use pattern matching in your function:
    • Hint / 提示:在你的函数中使用模式匹配:
#![allow(unused)]
fn main() {
match op {
-    Operation::Add(a, b) => { /* your code */ },
+    Operation::Add(a, b) => { /* your code / 你的代码 */ },
-    Operation::Subtract(a, b) => { /* your code */ },
+    Operation::Subtract(a, b) => { /* your code / 你的代码 */ },
}
}
  • Solution (click to expand)
  • Solution (click to expand) / 解决方案(点击展开)
enum Operation {
    Add(u64, u64),
    Subtract(u64, u64),
}

enum CalcResult {
    Ok(u64),
    Invalid(String),
}

fn calculate(op: Operation) -> CalcResult {
    match op {
        Operation::Add(a, b) => CalcResult::Ok(a + b),
        Operation::Subtract(a, b) => {
            if a >= b {
                CalcResult::Ok(a - b)
            } else {
                CalcResult::Invalid("Underflow".to_string())
            }
        }
    }
}

fn main() {
    match calculate(Operation::Add(10, 20)) {
        CalcResult::Ok(result) => println!("10 + 20 = {result}"),
        CalcResult::Invalid(msg) => println!("Error: {msg}"),
    }
    match calculate(Operation::Subtract(5, 10)) {
        CalcResult::Ok(result) => println!("5 - 10 = {result}"),
        CalcResult::Invalid(msg) => println!("Error: {msg}"),
    }
}
// Output / 输出:
// 10 + 20 = 30
// Error: Underflow
    • impl can define methods associated for types like struct, enum, etc
    • impl can define methods associated for types like struct, enum, etc / impl 可以为 structenum 等类型定义关联方法
  • - The methods may optionally take ```self``` as a parameter. ```self``` is conceptually similar to passing a pointer to the struct as the first parameter in C, or ```this``` in C++
    
  • - The methods may optionally take ```self``` as a parameter. ```self``` is conceptually similar to passing a pointer to the struct as the first parameter in C, or ```this``` in C++ / 方法可以可选地接收 ```self``` 作为参数。从概念上讲,```self``` 类似于 C 中将指向结构体的指针作为第一个参数传递,或者是 C++ 中的 ```this```
    
  • - The reference to ```self``` can be immutable (default: ```&self```), mutable (```&mut self```), or ```self``` (transferring ownership)
    
  • - The reference to ```self``` can be immutable (default: ```&self```), mutable (```&mut self```), or ```self``` (transferring ownership) / 对 ```self``` 的引用可以是不可变的(默认:```&self```)、可变的(```&mut self```)或 ```self```(转移所有权)
    
  • - The ```Self``` keyword can be used a shortcut to imply the type
    
  • - The ```Self``` keyword can be used a shortcut to imply the type / ```Self``` 关键字可用作指代该类型的快捷方式
    
struct Point {x: u32, y: u32}
impl Point {
    fn new(x: u32, y: u32) -> Self {
        Point {x, y}
    }
    fn increment_x(&mut self) {
        self.x += 1;
    }
}
fn main() {
    let mut p = Point::new(10, 20);
    p.increment_x();
}
  • 🟡 Intermediate — requires understanding move vs borrow from method signatures
  • 🟡 Intermediate / 中级 —— 需要从方法签名中理解移动 vs 借用
    • Implement the following associated methods for Point
    • Implement the following associated methods for Point / 为 Point 实现以下关联方法:
  • - ```add()``` will take another ```Point``` and will increment the x and y values in place (hint: use ```&mut self```)
    
  • - ```add()``` will take another ```Point``` and will increment the x and y values in place (hint: use ```&mut self```) / ```add()``` 将接收另一个 ```Point``` 并就地增加 x 和 y 值(提示:使用 ```&mut self```)
    
  • - ```transform()``` will consume an existing ```Point``` (hint: use ```self```) and return a new ```Point``` by squaring the x and y
    
  • - ```transform()``` will consume an existing ```Point``` (hint: use ```self```) and return a new ```Point``` by squaring the x and y / ```transform()``` 将消耗现有的 ```Point```(提示:使用 ```self```)并通过对 x 和 y 求平方返回一个新的 ```Point```
    
  • Solution (click to expand)
  • Solution (click to expand) / 解决方案(点击展开)
struct Point { x: u32, y: u32 }

impl Point {
    fn new(x: u32, y: u32) -> Self {
        Point { x, y }
    }
    fn add(&mut self, other: &Point) {
        self.x += other.x;
        self.y += other.y;
    }
    fn transform(self) -> Point {
        Point { x: self.x * self.x, y: self.y * self.y }
    }
}

fn main() {
    let mut p1 = Point::new(2, 3);
    let p2 = Point::new(10, 20);
    p1.add(&p2);
    println!("After add: x={}, y={}", p1.x, p1.y);           // x=12, y=23
    let p3 = p1.transform();
    println!("After transform: x={}, y={}", p3.x, p3.y);     // x=144, y=529
-    // p1 is no longer accessible — transform() consumed it
+    // p1 is no longer accessible — transform() consumed it / p1 不再可访问 —— transform() 消耗了它
}

Rust memory management / Rust 内存管理

What you’ll learn / 你将学到: Rust’s ownership system — the single most important concept in the language. After this chapter you’ll understand move semantics, borrowing rules, and the Drop trait. If you grasp this chapter, the rest of Rust follows naturally. If you’re struggling, re-read it — ownership clicks on the second pass for most C/C++ developers.

Rust 的所有权系统 —— 该语言中最重要的单一概念。学完本章后,你将理解移动语义、借用规则以及 Drop trait。如果你掌握了本章内容,Rust 的其余部分就会自然而然地理解。如果你感到困惑,请重读一遍 —— 对于大多数 C/C++ 开发者来说,所有权概念通常在读第二遍时才会真正“开悟”。

  • Memory management in C/C++ is a source of bugs / C/C++ 中的内存管理是 bug 的温床:
  • - In C: memory is allocated with `malloc()` and freed with `free()`. No checks against dangling pointers, use-after-free, or double-free
    
  • - In C: memory is allocated with `malloc()` and freed with `free()`. No checks against dangling pointers, use-after-free, or double-free / 在 C 中:使用 `malloc()` 分配内存,使用 `free()` 释放内存。没有针对悬垂指针、读取已释放内存(use-after-free)或二次释放(double-free)的检查
    
  • - In C++: RAII (Resource Acquisition Is Initialization) and smart pointers help, but `std::move(ptr)` compiles even after the move — use-after-move is UB
    
  • - In C++: RAII (Resource Acquisition Is Initialization) and smart pointers help, but `std::move(ptr)` compiles even after the move — use-after-move is UB / 在 C++ 中:RAII(资源获取即初始化)和智能指针有所帮助,但 `std::move(ptr)` 在移动后仍然可以编译 —— 移动后使用(use-after-move)是未定义行为(UB)
    
  • Rust makes RAII foolproof / Rust 让 RAII 变得万无一失
  • - Move is **destructive** — the compiler refuses to let you touch the moved-from variable
    
  • - Move is **destructive** — the compiler refuses to let you touch the moved-from variable / 移动是**破坏性**的 —— 编译器拒绝让你触碰已经移出的变量
    
  • - No Rule of Five needed (no copy ctor, move ctor, copy assign, move assign, destructor)
    
  • - No Rule of Five needed (no copy ctor, move ctor, copy assign, move assign, destructor) / 不需要遵守 “Rule of Five”(不需要定义拷贝构造、移动构造、拷贝赋值、移动赋值、析构函数)
    
  • - Rust gives complete control of memory allocation, but enforces safety at **compile time**
    
  • - Rust gives complete control of memory allocation, but enforces safety at **compile time** / Rust 赋予了对内存分配的完全控制权,但在**编译时**强制执行安全性
    
  • - This is done by a combination of mechanisms including ownership, borrowing, mutability and lifetimes
    
  • - This is done by a combination of mechanisms including ownership, borrowing, mutability and lifetimes / 这是由包括所有权、借用、可变性和生命周期在内的多种机制结合实现的
    
  • - Rust runtime allocations can happen both on the stack and the heap
    
  • - Rust runtime allocations can happen both on the stack and the heap / Rust 运行时分配既可以发生在栈上,也可以发生在堆上
    
  • For C++ developers — Smart Pointer Mapping:

  • For C++ developers — Smart Pointer Mapping / C++ 开发者 —— 智能指针映射:

-> | C++ | Rust | Safety Improvement | +| C++ | Rust | Safety Improvement / 安全提升 | |———|–––––|–––––––––––| -| std::unique_ptr<T> | Box<T> | No use-after-move possible | +| std::unique_ptr<T> | Box<T> | No use-after-move possible / 不可能出现移动后使用 | -| std::shared_ptr<T> | Rc<T> (single-thread) | No reference cycles by default | +| std::shared_ptr<T> | Rc<T> (single-thread) | No reference cycles by default / 默认无引用循环 | -| std::shared_ptr<T> (thread-safe) | Arc<T> | Explicit thread-safety | +| std::shared_ptr<T> (thread-safe) | Arc<T> | Explicit thread-safety / 显式的线程安全 | -| std::weak_ptr<T> | Weak<T> | Must check validity | +| std::weak_ptr<T> | Weak<T> | Must check validity / 必须检查有效性 | -| Raw pointer | *const T / *mut T | Only in unsafe blocks | +| Raw pointer / 原始指针 | *const T / *mut T | Only in unsafe blocks / 仅限 unsafe 块中使用 |

  • For C developers: Box<T> replaces malloc/free pairs. Rc<T> replaces manual reference counting. Raw pointers exist but are confined to unsafe blocks.

  • For C developers / 对于 C 开发者:Box<T> 取代了 malloc/free 配对。Rc<T> 取代了手动引用计数。原始指针存在,但被限制在 unsafe 块中。

    • Recall that Rust only permits a single mutable reference to a variable and multiple read-only references
    • Recall that Rust only permits a single mutable reference to a variable and multiple read-only references / 回想一下,Rust 只允许一个变量有一个可变引用或多个只读引用
  • - The initial declaration of the variable establishes ```ownership```
    
  • - The initial declaration of the variable establishes ```ownership``` / 变量的初始声明建立了```所有权(ownership)```
    
  • - Subsequent references ```borrow``` from the original owner. The rule is that the scope of the borrow can never exceed the owning scope. In other words, the ```lifetime``` of a borrow cannot exceed the owning lifetime
    
  • - Subsequent references ```borrow``` from the original owner. The rule is that the scope of the borrow can never exceed the owning scope. In other words, the ```lifetime``` of a borrow cannot exceed the owning lifetime / 随后的引用从原始所有者那里```借用(borrow)```。规则是借用的范围永远不能超过所有权范围。换句话说,借用的```生命周期(lifetime)```不能超过所有权的生命周期
    
fn main() {
-    let a = 42; // Owner
+    let a = 42; // Owner / 所有者
-    let b = &a; // First borrow
+    let b = &a; // First borrow / 第一次借用
    {
        let aa = 42;
-        let c = &a; // Second borrow; a is still in scope
+        let c = &a; // Second borrow; a is still in scope / 第二次借用;a 仍在作用域内
-        // Ok: c goes out of scope here
+        // Ok: c goes out of scope here / OK:c 在这里离开作用域
-        // aa goes out of scope here
+        // aa goes out of scope here / aa 在这里离开作用域
    }
-    // let d = &aa; // Will not compile unless aa is moved to outside scope
+    // let d = &aa; // Will not compile / 无法编译,除非 aa 被移出到外部作用域
-    // b implicitly goes out of scope before a
+    // b implicitly goes out of scope before a / b 在 a 之前隐式离开作用域
-    // a goes out of scope last
+    // a goes out of scope last / a 最后离开作用域
}
    • Rust can pass parameters to methods using several different mechanisms
    • Rust can pass parameters to methods using several different mechanisms / Rust 可以使用几种不同的机制向方法传递参数
  • - By value (copy): Typically types that can be trivially copied (ex: u8, u32, i8, i32)
    
  • - By value (copy): Typically types that can be trivially copied (ex: u8, u32, i8, i32) / 按值传递(拷贝):通常是那些可以被简单拷贝的类型(例如:u8, u32, i8, i32)
    
  • - By reference: This is the equivalent of passing a pointer to the actual value. This is also commonly known as borrowing, and the reference can be immutable (```&```), or mutable (```&mut```) 
    
  • - By reference: This is the equivalent of passing a pointer to the actual value. This is also commonly known as borrowing, and the reference can be immutable (```&```), or mutable (```&mut```) / 按引用传递:这相当于传递指向实际值的指针。这也通常被称为借用,引用可以是不可变的(```&```)或可变的(```&mut```)
    
  • - By moving: This transfers "ownership" of the value to the function. The caller can no longer reference the original value
    
  • - By moving: This transfers "ownership" of the value to the function. The caller can no longer reference the original value / 按移动传递:这会将值的“所有权”转移给函数。调用者不能再引用原始值
    
fn foo(x: &u32) {
    println!("{x}");
}
fn bar(x: u32) {
    println!("{x}");
}
fn main() {
    let a = 42;
-    foo(&a);    // By reference
+    foo(&a);    // By reference / 按引用
-    bar(a);     // By value (copy)
+    bar(a);     // By value (copy) / 按值(拷贝)
}
    • Rust prohibits dangling references from methods
    • Rust 禁止从方法返回悬垂引用(dangling references)
  • - References returned by methods must still be in scope
    
  • - References returned by methods must still be in scope / 方法返回的引用必须仍在作用域内
    
  • - Rust will automatically ```drop``` a reference when it goes out of scope. 
    
  • - Rust will automatically ```drop``` a reference when it goes out of scope. / 引用离开作用域时,Rust 会自动将其 ```drop```(释放)。
    
fn no_dangling() -> &u32 {
-    // lifetime of a begins here
+    // lifetime of a begins here / a 的生命周期在这里开始
    let a = 42;
-    // Won't compile. lifetime of a ends here
+    // Won't compile. lifetime of a ends here / 无法编译。a 的生命周期在这里结束
    &a
}

fn ok_reference(a: &u32) -> &u32 {
-    // Ok because the lifetime of a always exceeds ok_reference()
+    // Ok because the lifetime of a always exceeds ok_reference() / OK,因为 a 的生命周期总是超过 ok_reference()
    a
}
fn main() {
-    let a = 42;     // lifetime of a begins here
+    let a = 42;     // lifetime of a begins here / a 的生命周期在这里开始
    let b = ok_reference(&a);
-    // lifetime of b ends here
+    // lifetime of b ends here / b 的生命周期在这里结束
-    // lifetime of a ends here
+    // lifetime of a ends here / a 的生命周期在这里结束
}
    • By default, Rust assignment transfers ownership
    • By default, Rust assignment transfers ownership / 默认情况下,Rust 的赋值操作会转移所有权
fn main() {
-    let s = String::from("Rust");    // Allocate a string from the heap
+    let s = String::from("Rust");    // Allocate a string from the heap / 从堆上分配一个字符串
-    let s1 = s; // Transfer ownership to s1. s is invalid at this point
+    let s1 = s; // Transfer ownership to s1. s is invalid at this point / 将所有权转移给 s1。此时 s 已失效
    println!("{s1}");
-    // This will not compile
+    // This will not compile / 以下代码无法编译
    //println!("{s}");
-    // s1 goes out of scope here and the memory is deallocated
+    // s1 goes out of scope here and the memory is deallocated / s1 在这里离开作用域,内存被释放
-    // s goes out of scope here, but nothing happens because it doesn't own anything
+    // s goes out of scope here, but nothing happens because it doesn't own anything / s 在这里离开作用域,但没有任何反应,因为它已不拥有任何东西
}
graph LR
-    subgraph "Before: let s1 = s"
+    subgraph "Before: let s1 = s / 之前"
-        S["s (stack)<br/>ptr"] -->|"owns"| H1["Heap: R u s t"]
+        S["s (stack)<br/>ptr / 指子"] -->|"owns / 指向"| H1["Heap / 堆: R u s t"]
     end
 
-    subgraph "After: let s1 = s"
+    subgraph "After: let s1 = s / 之后"
-        S_MOVED["s (stack)<br/>⚠️ MOVED"] -.->|"invalid"| H2["Heap: R u s t"]
+        S_MOVED["s (stack)<br/>⚠️ MOVED / 已移动"] -.->|"invalid / 无效"| H2["Heap / 堆: R u s t"]
-        S1["s1 (stack)<br/>ptr"] -->|"now owns"| H2
+        S1["s1 (stack)<br/>ptr / 指子"] -->|"now owns / 接手指向"| H2
     end
 
     style S_MOVED fill:#ff6b6b,color:#000,stroke:#333
     style S1 fill:#51cf66,color:#000,stroke:#333
     style H2 fill:#91e5a3,color:#000,stroke:#333
  • After let s1 = s, ownership transfers to s1. The heap data stays put — only the stack pointer moves. s is now invalid.
  • 执行 let s1 = s 后,所有权转移到了 s1。堆上的数据不动 —— 仅仅是栈上的指针发生了移动。s 现已失效。

fn foo(s : String) {
    println!("{s}");
-    // The heap memory pointed to by s will be deallocated here
+    // The heap memory pointed to by s will be deallocated here / s 指向的堆内存将在这里被释放
}
fn bar(s : &String) {
    println!("{s}");
-    // Nothing happens -- s is borrowed
+    // Nothing happens -- s is borrowed / 没发生什么 —— s 是借用的
}
fn main() {
-    let s = String::from("Rust string move example");    // Allocate a string from the heap
+    let s = String::from("Rust string move example");    // Allocate a string from the heap / 从堆上分配一个字符串
-    foo(s); // Transfers ownership; s is invalid now
+    foo(s); // Transfers ownership; s is invalid now / 转移所有权;s 现在已失效
-    // println!("{s}");  // will not compile
+    // println!("{s}");  // will not compile / 无法编译
-    let t = String::from("Rust string borrow example");
+    let t = String::from("Rust string borrow example"); // 借用示例
-    bar(&t);    // t continues to hold ownership
+    bar(&t);    // t continues to hold ownership / t 继续持有所有权
    println!("{t}"); 
}
    • It is possible to transfer ownership by moving
    • It is possible to transfer ownership by moving / 可以通过移动来转移所有权
  • - It is illegal to reference outstanding references after the move is completed
    
  • - It is illegal to reference outstanding references after the move is completed / 移动完成后,引用已失效的(外挂)引用是非法的
    
  • - Consider borrowing if a move is not desirable
    
  • - Consider borrowing if a move is not desirable / 如果不需要移动,请考虑借用
    
struct Point {
    x: u32,
    y: u32,
}
fn consume_point(p: Point) {
    println!("{} {}", p.x, p.y);
}
fn borrow_point(p: &Point) {
    println!("{} {}", p.x, p.y);
}
fn main() {
    let p = Point {x: 10, y: 20};
-    // Try flipping the two lines
+    // Try flipping the two lines / 试着调整这两行的顺序
    borrow_point(&p);
    consume_point(p);
}
    • The clone() method can be used to copy the original memory. The original reference continues to be valid (the downside is that we have 2x the allocation)
    • The clone() method can be used to copy the original memory. The original reference continues to be valid (the downside is that we have 2x the allocation) / clone() 方法可用于拷贝原始内存。原始引用继续有效(缺点是我们会有双倍的内存分配)
fn main() {
-    let s = String::from("Rust");    // Allocate a string from the heap
+    let s = String::from("Rust");    // Allocate a string from the heap / 从堆上分配一个字符串
-    let s1 = s.clone(); // Copy the string; creates a new allocation on the heap
+    let s1 = s.clone(); // Copy the string; creates a new allocation on the heap / 拷贝字符串;在堆上创建一个新的分配
    println!("{s1}");  
    println!("{s}");
-    // s1 goes out of scope here and the memory is deallocated
+    // s1 goes out of scope here and the memory is deallocated / s1 在这里离开作用域,内存被释放
-    // s goes out of scope here, and the memory is deallocated
+    // s goes out of scope here, and the memory is deallocated / s 在这里离开作用域,内存被释放
}
graph LR
-    subgraph "After: let s1 = s.clone()"
+    subgraph "After: let s1 = s.clone() / 之后"
-        S["s (stack)<br/>ptr"] -->|"owns"| H1["Heap: R u s t"]
+        S["s (stack)<br/>ptr / 指针"] -->|"owns / 指向"| H1["Heap / 堆: R u s t"]
-        S1["s1 (stack)<br/>ptr"] -->|"owns (copy)"| H2["Heap: R u s t"]
+        S1["s1 (stack)<br/>ptr / 指针"] -->|"owns (copy) / 指向(拷贝)"| H2["Heap / 堆: R u s t"]
     end
 
     style S fill:#51cf66,color:#000,stroke:#333
     style S1 fill:#51cf66,color:#000,stroke:#333
     style H1 fill:#91e5a3,color:#000,stroke:#333
     style H2 fill:#91e5a3,color:#000,stroke:#333
  • clone() creates a separate heap allocation. Both s and s1 are valid — each owns its own copy.
  • clone() 创建了一个独立的堆分配。ss1 都有效 —— 它们各自拥有自己的拷贝。
    • Rust implements copy semantics for built-in types using the Copy trait
    • Rust implements copy semantics for built-in types using the Copy trait / Rust 使用 Copy trait 为内置类型实现拷贝语义
  • - Examples include u8, u32, i8, i32, etc. Copy semantics use "pass by value"
    
  • - Examples include u8, u32, i8, i32, etc. Copy semantics use "pass by value" / 示例包括 u8, u32, i8, i32 等。拷贝语义使用“按值传递”
    
  • - User defined data types can optionally opt into ```copy``` semantics using the ```derive``` macro with to automatically implement the ```Copy``` trait
    
  • - User defined data types can optionally opt into ```copy``` semantics using the ```derive``` macro with to automatically implement the ```Copy``` trait / 用户定义的数据类型可以可选地通过使用 ```derive``` 宏自动实现 ```Copy``` trait 来加入 ```copy``` 语义
    
  • - The compiler will allocate space for the copy following a new assignment
    
  • - The compiler will allocate space for the copy following a new assignment / 在新的赋值操作后,编译器将为拷贝分配空间
    
- // Try commenting this out to see the change in let p1 = p; belw
+ // Try commenting this out to see the change in let p1 = p; below
+ // 试着注释掉这一行,看看下面 let p1 = p; 的变化
#[derive(Copy, Clone, Debug)]   // We'll discuss this more later
struct Point{x: u32, y:u32}
fn main() {
    let p = Point {x: 42, y: 40};
-    let p1 = p;     // This will perform a copy now instead of move
+    let p1 = p;     // This will perform a copy now instead of move / 现在这将执行拷贝而不是移动
    println!("p: {p:?}");
    println!("p1: {p:?}");
-    let p2 = p1.clone();    // Semantically the same as copy
+    let p2 = p1.clone();    // Semantically the same as copy / 在语义上与拷贝相同
}
    • Rust automatically calls the drop() method at the end of scope
    • Rust automatically calls the drop() method at the end of scope / Rust 在作用域结束时自动调用 drop() 方法
  • - `drop` is part of a generic trait called `Drop`. The compiler provides a blanket NOP implementation for all types, but types can override it. For example, the `String` type overrides it to release heap-allocated memory
    
  • - `drop` is part of a generic trait called `Drop`. The compiler provides a blanket NOP implementation for all types, but types can override it. For example, the `String` type overrides it to release heap-allocated memory / `drop` 是名为 `Drop` 的通用 trait 的一部分。编译器为所有类型提供了默认的 NOP(空操作)实现,但各类型可以覆盖它。例如,`String` 类型覆盖了它以释放堆分配的内存
    
  • - For C developers: this replaces the need for manual `free()` calls — resources are automatically released when they go out of scope (RAII)
    
  • - For C developers / 对于 C 开发者:这取代了手动调用 `free()` 的需要 —— 资源在离开作用域时会自动释放(RAII)
    
    • Key safety: You cannot call .drop() directly (the compiler forbids it). Instead, use drop(obj) which moves the value into the function, runs its destructor, and prevents any further use — eliminating double-free bugs
    • Key safety / 关键安全特性: 你不能直接调用 .drop()(编译器禁止这样做)。相反,请使用 drop(obj),这会将值移动到函数中,运行其析构函数,并阻止进一步使用 —— 从而消除了二次释放 bug
  • For C++ developers: Drop maps directly to C++ destructors (~ClassName()):

  • For C++ developers — Drop Mapping / C++ 开发者 —— Drop 映射: Drop 直接映射到 C++ 析构函数(~ClassName()):

-| | C++ destructor | Rust Drop | +| | C++ destructor / C++ 析构函数 | Rust Drop | |—|—|—| -| Syntax | ~MyClass() { ... } | impl Drop for MyType { fn drop(&mut self) { ... } } | +| Syntax / 语法 | ~MyClass() { ... } | impl Drop for MyType { fn drop(&mut self) { ... } } | -| When called | End of scope (RAII) | End of scope (same) | +| When called / 何时调用 | End of scope (RAII) / 作用域结束 | End of scope (same) / 同上 | -| Called on move | Source left in “valid but unspecified” state — destructor still runs on the moved-from object | Source is gone — no destructor call on moved-from value | +| Called on move / 在移动时调用 | Source left in “valid but unspecified” state — destructor still runs on the moved-from object / 源对象处于“有效但未指定”状态 —— 析构函数仍会在已移出的对象上运行 | Source is gone — no destructor call on moved-from value / 源对象已消失 —— 不会在已移出的值上调用析构函数 | -| Manual call | obj.~MyClass() (dangerous, rarely used) | drop(obj) (safe — takes ownership, calls drop, prevents further use) | +| Manual call / 手动调用 | obj.~MyClass() (dangerous, rarely used) / 危险且罕见 | drop(obj) (safe — takes ownership, calls drop, prevents further use) / 安全 —— 获取所有权,调用 drop,阻止后续使用 | -| Order | Reverse declaration order | Reverse declaration order (same) | +| Order / 顺序 | Reverse declaration order / 与声明顺序相反 | Reverse declaration order (same) / 同上 | -| Rule of Five | Must manage copy ctor, move ctor, copy assign, move assign, destructor | Only Drop — compiler handles move semantics, and Clone is opt-in | +| Rule of Five | Must manage copy ctor, move ctor, copy assign, move assign, destructor / 必须管理拷贝/移动构造、拷贝/移动赋值、析构函数 | Only Drop — compiler handles move semantics, and Clone is opt-in / 只有 Drop —— 编译器处理移动语义,Clone 是可选加入的 | -| Virtual dtor needed? | Yes, if deleting through base pointer | No — no inheritance, so no slicing problem | +| Virtual dtor needed? / 需要虚析构函数? | Yes / 是,如果通过基类指针删除 | No / 否 —— 没有继承,所以没有对象切割问题 |

struct Point {x: u32, y:u32}

- // Equivalent to: ~Point() { printf("Goodbye point x:%u, y:%u\n", x, y); }
+ // Equivalent to / 等同于:~Point() { printf("Goodbye point x:%u, y:%u\n", x, y); }
impl Drop for Point {
    fn drop(&mut self) {
        println!("Goodbye point x:{}, y:{}", self.x, self.y);
    }
}
fn main() {
    let p = Point{x: 42, y: 42};
    {
        let p1 = Point{x:43, y: 43};
        println!("Exiting inner block");
-        // p1.drop() called here — like C++ end-of-scope destructor
+        // p1.drop() called here — like C++ end-of-scope destructor / p1.drop() 在这里被调用 —— 类似于 C++ 的作用域结束析构函数
    }
    println!("Exiting main");
-    // p.drop() called here
+    // p.drop() called here / p.drop() 在这里被调用
}
  • 🟡 Intermediate — experiment freely; the compiler will guide you
  • 🟡 Intermediate / 中级 —— 自由实验;编译器会引导你
    • Create your own experiments with Point with and without Copy in #[derive(Debug)] in the below make sure you understand the differences. The idea is to get a solid understanding of how move vs. copy works, so make sure to ask
    • Create your own experiments with Point with and without Copy in #[derive(Debug)] in the below make sure you understand the differences. The idea is to get a solid understanding of how move vs. copy works, so make sure to ask / 用带有和不带有 CopyPoint 进行你自己的实验,确保你理解其中的差异。其目的是让你牢固理解移动与拷贝的工作原理。
    • Implement a custom Drop for Point that sets x and y to 0 in drop. This is a pattern that’s useful for releasing locks and other resources for example
    • Implement a custom Drop for Point that sets x and y to 0 in drop. This is a pattern that’s useful for releasing locks and other resources for example / 为 Point 实现一个自定义的 Drop,在 drop 中将 x 和 y 设置为 0。例如,这是一种对于释放锁和其他资源非常有用的模式。
struct Point{x: u32, y: u32}
fn main() {
    // Create Point, assign it to a different variable, create a new scope,
    // pass point to a function, etc.
+    // 创建 Point,将其赋值给不同的变量,创建一个新作用域,
+    // 将 point 传递给函数等。
}
  • Solution (click to expand)
  • Solution (click to expand) / 解决方案(点击展开)
#[derive(Debug)]
struct Point { x: u32, y: u32 }

impl Drop for Point {
    fn drop(&mut self) {
        println!("Dropping Point({}, {})", self.x, self.y);
        self.x = 0;
        self.y = 0;
-        // Note: setting to 0 in drop demonstrates the pattern,
+        // Note: setting to 0 in drop demonstrates the pattern, / 注意:在 drop 中设置为 0 只是为了演示该模式
-        // but you can't observe these values after drop completes
+        // but you can't observe these values after drop completes / 但在 drop 完成后你无法观察到这些值
    }
}

fn consume(p: Point) {
    println!("Consuming: {:?}", p);
-    // p is dropped here
+    // p is dropped here / p 在这里被 drop
}

fn main() {
    let p1 = Point { x: 10, y: 20 };
-    let p2 = p1;  // Move — p1 is no longer valid
+    let p2 = p1;  // Move — p1 is no longer valid / 移动 —— p1 现已失效
-    // println!("{:?}", p1);  // Won't compile: p1 was moved
+    // println!("{:?}", p1);  // Won't compile: p1 was moved / 无法编译:p1 已被移动

    {
        let p3 = Point { x: 30, y: 40 };
        println!("p3 in inner scope: {:?}", p3);
-        // p3 is dropped here (end of scope)
+        // p3 is dropped here (end of scope) / p3 在这里被 drop(作用域结束)
    }

-    consume(p2);  // p2 is moved into consume and dropped there
+    consume(p2);  // p2 is moved into consume and dropped there / p2 被移动到 consume 函数中并在那里被 drop
-    // println!("{:?}", p2);  // Won't compile: p2 was moved
+    // println!("{:?}", p2);  // Won't compile: p2 was moved / 无法编译:p2 已被移动

-    // Now try: add #[derive(Copy, Clone)] to Point (and remove the Drop impl)
+    // Now try: add #[derive(Copy, Clone)] to Point (and remove the Drop impl) / 现在尝试:给 Point 添加 #[derive(Copy, Clone)](并移除 Drop 实现)
-    // and observe how p1 remains valid after let p2 = p1;
+    // and observe how p1 remains valid after let p2 = p1; / 然后观察在执行 let p2 = p1; 后 p1 如何保持有效
}
- // Output:
+ // Output / 输出:
// p3 in inner scope: Point { x: 30, y: 40 }
// Dropping Point(30, 40)
// Consuming: Point { x: 10, y: 20 }
// Dropping Point(10, 20)

Rust lifetime and borrowing / Rust 生命周期与借用

What you’ll learn / 你将学到: How Rust’s lifetime system ensures references never dangle — from implicit lifetimes through explicit annotations to the three elision rules that make most code annotation-free. Understanding lifetimes here is essential before moving on to smart pointers in the next section.

Rust 的生命周期系统如何确保引用永远不会悬垂 —— 从隐式生命周期到显式标注,再到使大多数代码无需标注的三条省略规则。在进入下一节编写智能指针之前,理解生命周期至关重要。

  • Rust enforces a single mutable reference and any number of immutable references / Rust 强制执行单一可变引用和任意数量的不可变引用
  • - The lifetime of any reference must be at least as long as the original owning lifetime. These are implicit lifetimes and are inferred by the compiler (see https://doc.rust-lang.org/nomicon/lifetime-elision.html)
    
  • - The lifetime of any reference must be at least as long as the original owning lifetime. These are implicit lifetimes and are inferred by the compiler (see https://doc.rust-lang.org/nomicon/lifetime-elision.html) / 任何引用的生命周期必须至少与原始所有权的生存期一样长。这些是隐式生命周期,由编译器推导(参见 https://doc.rust-lang.org/nomicon/lifetime-elision.html)
    
fn borrow_mut(x: &mut u32) {
    *x = 43;
}
fn main() {
    let mut x = 42;
    let y = &mut x;
    borrow_mut(y);
-    let _z = &x; // Permitted because the compiler knows y isn't subsequently used
+    let _z = &x; // Permitted / 允许,因为编译器知道 y 随后不再被使用
-    //println!("{y}"); // Will not compile if this is uncommented
+    //println!("{y}"); // Will not compile / 如果取消注释,将无法编译
-    borrow_mut(&mut x); // Permitted because _z isn't used 
+    borrow_mut(&mut x); // Permitted / 允许,因为 _z 没被使用 
-    let z = &x; // Ok -- mutable borrow of x ended after borrow_mut() returned
+    let z = &x; // Ok -- mutable borrow of x ended / Ok —— x 的可变借用在 borrow_mut() 返回后结束
    println!("{z}");
}
    • Explicit lifetime annotations are needed when dealing with multiple lifetimes
    • Explicit lifetime annotations are needed when dealing with multiple lifetimes / 在处理多个生命周期时,需要显式的生命周期标注
  • - Lifetimes are denoted with `'` and can be any identifier (`'a`, `'b`, `'static`, etc.)
    
  • - Lifetimes are denoted with `'` and can be any identifier (`'a`, `'b`, `'static`, etc.) / 生命周期用 `'` 表示,可以是任何标识符(`'a`、`'b`、`'static` 等)
    
  • - The compiler needs help when it can't figure out how long references should live
    
  • - The compiler needs help when it can't figure out how long references should live / 当编译器无法确定引用应该存活多久时,需要提供帮助
    
    • Common scenario: Function returns a reference, but which input does it come from?
    • Common scenario / 常见场景:函数返回一个引用,但它来自哪个输入?
#[derive(Debug)]
struct Point {x: u32, y: u32}

- // Without lifetime annotation, this won't compile:
+ // Without lifetime annotation, this won't compile / 没有生命周期标注,以下代码无法编译:
// fn left_or_right(pick_left: bool, left: &Point, right: &Point) -> &Point

- // With lifetime annotation - all references share the same lifetime 'a
+ // With lifetime annotation - all references share the same lifetime 'a / 带有显式标注 —— 所有引用共享相同的生命周期 'a
fn left_or_right<'a>(pick_left: bool, left: &'a Point, right: &'a Point) -> &'a Point {
    if pick_left { left } else { right }
}

- // More complex: different lifetimes for inputs
+ // More complex: different lifetimes for inputs / 更复杂的情况:输入具有不同的生命周期
fn get_x_coordinate<'a, 'b>(p1: &'a Point, _p2: &'b Point) -> &'a u32 {
-    &p1.x  // Return value lifetime tied to p1, not p2
+    &p1.x  // Return value lifetime tied to p1, not p2 / 返回值的生命周期与 p1 绑定,而不是 p2
}

fn main() {
    let p1 = Point {x: 20, y: 30};
    let result;
    {
        let p2 = Point {x: 42, y: 50};
        result = left_or_right(true, &p1, &p2);
-        // This works because we use result before p2 goes out of scope
+        // This works because we use result before p2 goes out of scope / 这可以工作,因为我们在 p2 离开作用域之前使用了 result
        println!("Selected: {result:?}");
    }
-    // This would NOT work - result references p2 which is now gone:
+    // This would NOT work - result references p2 which is now gone / 这行不通 —— result 引用了现在已经消失的 p2:
    // println!("After scope: {result:?}");
}
    • Lifetime annotations are also needed for references in data structures
    • Lifetime annotations are also needed for references in data structures / 数据结构中的引用也需要生命周期标注
use std::collections::HashMap;
#[derive(Debug)]
struct Point {x: u32, y: u32}
struct Lookup<'a> {
    map: HashMap<u32, &'a Point>,
}
fn main() {
    let p = Point{x: 42, y: 42};
    let p1 = Point{x: 50, y: 60};
    let mut m = Lookup {map : HashMap::new()};
    m.map.insert(0, &p);
    m.map.insert(1, &p1);
    {
        let p3 = Point{x: 60, y:70};
-        //m.map.insert(3, &p3); // Will not compile
+        //m.map.insert(3, &p3); // Will not compile / 无法编译
-        // p3 is dropped here, but m will outlive
+        // p3 is dropped here, but m will outlive / p3 在这里被 drop,但 m 存活更久
    }
    for (k, v) in m.map {
        println!("{v:?}");
    }
-    // m is dropped here
+    // m is dropped here / m 在这里被 drop
-    // p1 and p are dropped here in that order
+    // p1 and p are dropped here in that order / p1 和 p 依次在这里被 drop
} 
  • 🟢 Starter — practice lifetime elision in action
  • 🟢 Starter / 入门级 —— 实践生命周期省略规则
  • Write a function fn first_word(s: &str) -> &str that returns the first whitespace-delimited word from a string. Think about why this compiles without explicit lifetime annotations (hint: elision rule #1 and #2).
  • 编写一个函数 fn first_word(s: &str) -> &str,返回字符串中第一个由空格分隔的单词。思考为什么这段代码在没有显式生命周期标注的情况下也能编译(提示:省略规则 #1 和 #2)。
  • Solution (click to expand)
  • Solution (click to expand) / 解决方案(点击展开)
fn first_word(s: &str) -> &str {
    // The compiler applies elision rules:
-    // Rule 1: input &str gets lifetime 'a → fn first_word(s: &'a str) -> &str
+    // Rule 1: input &str gets lifetime 'a / 规则1:输入 &str 获得生命周期 'a
-    // Rule 2: single input lifetime → output gets same → fn first_word(s: &'a str) -> &'a str
+    // Rule 2: single input lifetime → output gets same / 规则2:单个输入生命周期 -> 输出获得相同的生命周期
    match s.find(' ') {
        Some(pos) => &s[..pos],
        None => s,
    }
}

fn main() {
    let text = "hello world foo";
    let word = first_word(text);
    println!("First word: {word}");  // "hello"
    
    let single = "onlyone";
    println!("First word: {}", first_word(single));  // "onlyone"
}
  • 🟡 Intermediate — your first encounter with lifetime annotations
  • 🟡 Intermediate / 中级 —— 第一次接触生命周期标注
    • Create a structure that stores references to the slice of a &str
    • Create a structure that stores references to the slice of a &str / 创建一个存储 &str 切片引用的结构体
  • - Create a long ```&str``` and store references slices from it inside the structure
    
  • - Create a long ```&str``` and store references slices from it inside the structure / 创建一个长 ```&str``` 并在结构体内部存储其中的切片引用
    
  • - Write a function that accepts the structure and returns the contained slice
    
  • - Write a function that accepts the structure and returns the contained slice / 编写一个接收该结构体并返回包含切片的函数
    
- // TODO: Create a structure to store a reference to a slice
+ // TODO: Create a structure to store a reference to a slice / TODO:创建一个存储切片引用的结构体
struct SliceStore {

}
fn main() {
    let s = "This is long string";
    let s1 = &s[0..];
    let s2 = &s[1..2];
    // let slice = struct SliceStore {...};
    // let slice2 = struct SliceStore {...};
}
  • Solution (click to expand)
  • Solution (click to expand) / 解决方案(点击展开)
struct SliceStore<'a> {
    slice: &'a str,
}

impl<'a> SliceStore<'a> {
    fn new(slice: &'a str) -> Self {
        SliceStore { slice }
    }

    fn get_slice(&self) -> &'a str {
        self.slice
    }
}

fn main() {
    let s = "This is a long string";
    let store1 = SliceStore::new(&s[0..4]);   // "This"
    let store2 = SliceStore::new(&s[5..7]);   // "is"
    println!("store1: {}", store1.get_slice());
    println!("store2: {}", store2.get_slice());
}
- // Output:
+ // Output / 输出:
// store1: This
// store2: is

  • C programmers often ask: “If lifetimes are so important, why don’t most Rust functions
  • C 程序员经常问:“如果生命周期如此重要,为什么大多数 Rust 函数
  • have 'a annotations?“ The answer is lifetime elision — the compiler applies three
  • 不需要 'a 标注?”答案是生命周期省略(lifetime elision) —— 编译器应用三条
  • deterministic rules to infer lifetimes automatically.
  • 确定性规则来自动推导生命周期。
  • The Rust compiler applies these rules in order to function signatures. If all output
  • Rust 编译器按顺序对函数签名应用这些规则。如果应用规则后所有输出
  • lifetimes are determined after applying the rules, no annotations are needed.
  • 的生命周期都已确定,则无需标注。
flowchart TD
-    A["Function signature with references"] --> R1
+    A["Function signature with references<br/>带有引用的函数签名"] --> R1
-    R1["Rule 1: Each input reference<br/>gets its own lifetime<br/><br/>fn f(&str, &str)<br/>→ fn f<'a,'b>(&'a str, &'b str)"]
+    R1["Rule 1: Each input reference<br/>gets its own lifetime<br/><br/>规则 1:每个输入引用<br/>获得各自的生命周期"]
     R1 --> R2
-    R2["Rule 2: If exactly ONE input<br/>lifetime, assign it to ALL outputs<br/><br/>fn f(&str) → &str<br/>→ fn f<'a>(&'a str) → &'a str"]
+    R2["Rule 2: If exactly ONE input<br/>lifetime, assign it to ALL outputs<br/><br/>规则 2:如果只有一个输入生命周期,<br/>将其分配给所有输出"]
     R2 --> R3
-    R3["Rule 3: If one input is &self<br/>or &mut self, assign its lifetime<br/>to ALL outputs<br/><br/>fn f(&self, &str) → &str<br/>→ fn f<'a>(&'a self, &str) → &'a str"]
+    R3["Rule 3: If one input is &self<br/>or &mut self, assign its lifetime<br/>to ALL outputs<br/><br/>规则 3:如果输入中有 &self<br/>或 &mut self,将其生命周期分配给所有输出"]
-    R3 --> CHECK{{"All output lifetimes<br/>determined?"}}
+    R3 --> CHECK{{"All output lifetimes<br/>determined?<br/>所有输出生命周期都已确定?"}}
-    CHECK -->|"Yes"| OK["✅ No annotations needed"]
+    CHECK -->|"Yes / 是"| OK["✅ No annotations needed / 无需标注"]
-    CHECK -->|"No"| ERR["❌ Compile error:<br/>must annotate manually"]
+    CHECK -->|"No / 否"| ERR["❌ Compile error / 编译错误:<br/>必须手动标注"]
     
     style OK fill:#91e5a3,color:#000
     style ERR fill:#ff6b6b,color:#000
  • Rule 1 — each input reference gets its own lifetime parameter:
  • 规则 1 —— 每个输入引用获得自己的生命周期参数:
#![allow(unused)]
fn main() {
- // What you write:
+ // 你写的:
fn first_word(s: &str) -> &str { ... }

- // What the compiler sees after Rule 1:
+ // 编译器在规则 1 之后看到的:
fn first_word<'a>(s: &'a str) -> &str { ... }
- // Only one input lifetime → Rule 2 applies
+ // 只有一个输入生命周期 -> 应用规则 2
}
  • Rule 2 — single input lifetime propagates to all outputs:
  • 规则 2 —— 单一输入生命周期传播到所有输出:
#![allow(unused)]
fn main() {
- // After Rule 2:
+ // 规则 2 之后:
fn first_word<'a>(s: &'a str) -> &'a str { ... }
- // ✅ All output lifetimes determined — no annotation needed!
+ // ✅ 所有输出生命周期已确定 —— 无需标注!
}
  • Rule 3&self lifetime propagates to outputs:
  • 规则 3 —— &self 生命周期传播到输出:
#![allow(unused)]
fn main() {
- // What you write:
+ // 你写的:
impl SliceStore<'_> {
    fn get_slice(&self) -> &str { self.slice }
}

- // What the compiler sees after Rules 1 + 3:
+ // 编译器在规则 1 + 3 之后看到的:
impl SliceStore<'_> {
    fn get_slice<'a>(&'a self) -> &'a str { self.slice }
}
- // ✅ No annotation needed — &self lifetime used for output
+ // ✅ 无需标注 —— 输出使用了 &self 的生命周期
}
  • When elision fails — you must annotate:
  • 省略失败的情况 —— 你必须手动标注:
#![allow(unused)]
fn main() {
- // Two input references, no &self → Rules 2 and 3 don't apply
+ // 两个输入引用,且没有 &self -> 规则 2 和 3 不适用
// fn longest(a: &str, b: &str) -> &str  ← WON'T COMPILE

- // Fix: tell the compiler which input the output borrows from
+ // 修复:告诉编译器输出借用了哪个输入
fn longest<'a>(a: &'a str, b: &'a str) -> &'a str {
    if a.len() >= b.len() { a } else { b }
}
}
  • In C, every pointer is independent — the programmer mentally tracks which allocation
  • 在 C 中,每个指针都是独立的 —— 程序员需要在脑海中跟踪每个指针
  • each pointer refers to, and the compiler trusts you completely. In Rust, lifetimes make
  • 指向哪个分配,而编译器完全信任你。在 Rust 中,生命周期使这种跟踪变为
  • this tracking explicit and compiler-verified:
  • 显式的并经过编译器验证

-| C | Rust | What happens | +| C | Rust | What happens / 发生了什么 | |—|——|———––| -| char* get_name(struct User* u) | fn get_name(&self) -> &str | Rule 3 elides: output borrows from self | +| char* get_name(struct User* u) | fn get_name(&self) -> &str | Rule 3 elides / 规则3省略:输出借用自 self | -| char* concat(char* a, char* b) | fn concat<'a>(a: &'a str, b: &'a str) -> &'a str | Must annotate — two inputs | +| char* concat(char* a, char* b) | fn concat<'a>(a: &'a str, b: &'a str) -> &'a str | Must annotate / 必须标注 —— 有两个输入 | -| void process(char* in, char* out) | fn process(input: &str, output: &mut String) | No output reference — no lifetime needed | +| void process(char* in, char* out) | fn process(input: &str, output: &mut String) | No output reference / 无输出引用 —— 无需生命周期 | -| char* buf; /* who owns this? */ | Compile error if lifetime is wrong | Compiler catches dangling pointers | +| char* buf; /* who owns this? */ | Compile error / 编译错误(如果生命周期错误) | Compiler catches dangling pointers / 编译器捕获悬垂指针 |

  • 'static means the reference is valid for the entire program duration. It’s the
  • 'static 意味着引用在整个程序运行期间都有效。它相当于
  • Rust equivalent of a C global or string literal:
  • Rust 中的 C 全局变量或字符串字面量:
#![allow(unused)]
fn main() {
- // String literals are always 'static — they live in the binary's read-only section
+ // String literals are always 'static / 字符串字面量始终是 'static —— 它们存在于二进制文件的只读段中
- let s: &'static str = "hello";  // Same as: static const char* s = "hello"; in C
+ let s: &'static str = "hello";  // 等同于 C 中的 static const char* s = "hello";

- // Constants are also 'static
+ // Constants are also 'static / 常量也是 'static
static GREETING: &str = "hello";

- // Common in trait bounds for thread spawning:
+ // 在线程生成的 trait 约束中很常见:
fn spawn<F: FnOnce() + Send + 'static>(f: F) { /* ... */ }
- // 'static here means: "the closure must not borrow any local variables"
+ // 这里的 'static 意味着:“闭包不得借用任何局部变量”
- // (either move them in, or use only 'static data)
+ // (要么把它们 move 进去,要么只使用 'static 数据)
}
  • 🟡 Intermediate
  • 🟡 Intermediate / 中级
  • For each function signature below, predict whether the compiler can elide lifetimes.
  • 对于下面每个函数签名,预测编译器是否可以省略生命周期。
  • If not, add the necessary annotations:
  • 如果不能,请添加必要的标注:
#![allow(unused)]
fn main() {
- // 1. Can the compiler elide?
+ // 1. 编译器可以省略吗?
fn trim_prefix(s: &str) -> &str { &s[1..] }

- // 2. Can the compiler elide?
+ // 2. 编译器可以省略吗?
fn pick(flag: bool, a: &str, b: &str) -> &str {
    if flag { a } else { b }
}

- // 3. Can the compiler elide?
+ // 3. 编译器可以省略吗?
struct Parser { data: String }
impl Parser {
    fn next_token(&self) -> &str { &self.data[..5] }
}

- // 4. Can the compiler elide?
+ // 4. 编译器可以省略吗?
fn split_at(s: &str, pos: usize) -> (&str, &str) {
    (&s[..pos], &s[pos..])
}
}
  • Solution (click to expand)
  • Solution (click to expand) / 解决方案(点击展开)
- // 1. YES — Rule 1 gives 'a to s, Rule 2 propagates to output
+ // 1. 是 —— 规则 1 给 s 分配 'a,规则 2 传播到输出
fn trim_prefix(s: &str) -> &str { &s[1..] }

- // 2. NO — Two input references, no &self. Must annotate:
+ // 2. 否 —— 两个输入引用,且没有 &self。必须手动标注:
fn pick<'a>(flag: bool, a: &'a str, b: &'a str) -> &'a str {
    if flag { a } else { b }
}

- // 3. YES — Rule 1 gives 'a to &self, Rule 3 propagates to output
+ // 3. 是 —— 规则 1 给 &self 分配 'a,规则 3 传播到输出
impl Parser {
    fn next_token(&self) -> &str { &self.data[..5] }
}

- // 4. YES — Rule 1 gives 'a to s (only one input reference),
+ // 4. 是 —— 规则 1 给 s 分配 'a(只有一个输入引用),
- //    Rule 2 propagates to BOTH outputs. Both slices borrow from s.
+ //    规则 2 会传播到两个输出。两个切片都借用自 s。
fn split_at(s: &str, pos: usize) -> (&str, &str) {
    (&s[..pos], &s[pos..])
}

Rust Box<T> / Rust Box<T>

What you’ll learn / 你将学到: Rust’s smart pointer types — Box<T> for heap allocation, Rc<T> for shared ownership, and Cell<T>/RefCell<T> for interior mutability. These build on the ownership and lifetime concepts from the previous sections. You’ll also see a brief introduction to Weak<T> for breaking reference cycles.

Rust 的智能指针类型 —— 用于堆分配的 Box<T>、用于共享所有权的 Rc<T>,以及用于内部可变性的 Cell<T>/RefCell<T>。这些都建立在前面章节的所有权和生命周期概念之上。你还将看到通过 Weak<T> 打破引用循环的简要介绍。

  • Why Box<T>? In C, you use malloc/free for heap allocation. In C++, std::unique_ptr<T> wraps new/delete. Rust’s Box<T> is the equivalent — a heap-allocated, single-owner pointer that is automatically freed when it goes out of scope. Unlike malloc, there’s no matching free to forget. Unlike unique_ptr, there’s no use-after-move — the compiler prevents it entirely.
  • Why Box<T>? / 为什么使用 Box<T> 在 C 中,你使用 malloc/free 进行堆分配。在 C++ 中,std::unique_ptr<T> 包装了 new/delete。Rust 的 Box<T> 是其等效物 —— 一个堆分配的、单一所有者的指针,当它离开作用域时会自动释放。与 malloc 不同,没有配套的 free 需要去操心。与 unique_ptr 不同,没有“移动后使用(use-after-move)”的问题 —— 编译器完全阻止了这种情况。
  • When to use Box vs stack allocation:
  • When to use Box vs stack allocation / 何时使用 Box 而不是栈分配:
    • The contained type is large and you don’t want to copy it on the stack
    • The contained type is large and you don’t want to copy it on the stack / 包含的类型很大,你不想在栈上拷贝它
    • You need a recursive type (e.g., a linked list node that contains itself)
    • You need a recursive type (e.g., a linked list node that contains itself) / 你需要一个递归类型(例如,包含自身的链表节点)
    • You need trait objects (Box<dyn Trait>)
    • You need trait objects (Box<dyn Trait>) / 你需要 trait 对象(Box<dyn Trait>
    • Box<T> can be use to create a pointer to a heap allocated type. The pointer is always a fixed size regardless of the type of <T>
    • Box<T> can be use to create a pointer to a heap allocated type. The pointer is always a fixed size regardless of the type of <T> / Box<T> 可用于创建指向堆分配类型的指针。无论 <T> 是什么类型,指针的大小始终是固定的
fn main() {
-    // Creates a pointer to an integer (with value 42) created on the heap
+    // Creates a pointer to an integer (with value 42) created on the heap / 创建指向堆上整数(值为 42)的指针
    let f = Box::new(42);
    println!("{} {}", *f, f);
-    // Cloning a box creates a new heap allocation
+    // Cloning a box creates a new heap allocation / 克隆 box 会创建一个新的堆分配
    let mut g = f.clone();
    *g = 43;
    println!("{f} {g}");
-    // g and f go out of scope here and are automatically deallocated
+    // g and f go out of scope here and are automatically deallocated / g 和 f 在这里离开作用域并自动释放
}
graph LR
-    subgraph "Stack"
+    subgraph "Stack / 栈"
-        F["f: Box&lt;i32&gt;"]
+        F["f: Box&lt;i32&gt; / 指针"]
-        G["g: Box&lt;i32&gt;"]
+        G["g: Box&lt;i32&gt; / 指针"]
     end
 
-    subgraph "Heap"
+    subgraph "Heap / 堆"
         HF["42"]
         HG["43"]
     end
 
-    F -->|"owns"| HF
+    F -->|"owns / 指向"| HF
-    G -->|"owns (cloned)"| HG
+    G -->|"owns (cloned) / 指向(克隆)"| HG
 
     style F fill:#51cf66,color:#000,stroke:#333
     style G fill:#51cf66,color:#000,stroke:#333
     style HF fill:#91e5a3,color:#000,stroke:#333
     style HG fill:#91e5a3,color:#000,stroke:#333
// C - Manual memory management, potential issues
+// C - 手动内存管理,潜在问题
void c_pointer_problems() {
    int* ptr1 = malloc(sizeof(int));
    *ptr1 = 42;
    
-    int* ptr2 = ptr1;  // Both point to same memory
+    int* ptr2 = ptr1;  // Both point to same memory / 两个指针指向同一块内存
-    int* ptr3 = ptr1;  // Three pointers to same memory
+    int* ptr3 = ptr1;  // Three pointers to same memory / 三个指针指向同一块内存
    
-    free(ptr1);        // Frees the memory
+    free(ptr1);        // Frees the memory / 释放内存
    
-    *ptr2 = 43;        // Use after free - undefined behavior!
+    *ptr2 = 43;        // Use after free / 释放后使用 —— 未定义行为!
-    *ptr3 = 44;        // Use after free - undefined behavior!
+    *ptr3 = 44;        // Use after free / 释放后使用 —— 未定义行为!
}
  • For C++ developers: Smart pointers help, but don’t prevent all issues:

  • For C++ developers / C++ 开发者注意: 智能指针有所帮助,但不能防止所有问题:

// C++ - Smart pointers help, but don’t prevent all issues +// C++ - 智能指针有所帮助,但不能防止所有问题 void cpp_pointer_issues() { auto ptr1 = std::make_unique(42);

  • // auto ptr2 = ptr1; // Compile error: unique_ptr not copyable
  • // auto ptr2 = ptr1; // Compile error / 编译错误:unique_ptr 不可拷贝
  • auto ptr2 = std::move(ptr1); // OK: ownership transferred
  • auto ptr2 = std::move(ptr1); // OK: ownership transferred / OK:所有权转移
  • // But C++ still allows use-after-move:
  • // But C++ still allows use-after-move / 但 C++ 仍然允许移动后使用:
  • // std::cout << *ptr1; // Compiles! But undefined behavior!
  • // std::cout << *ptr1; // Compiles! / 能编译!但会导致未定义行为!
  • // shared_ptr aliasing:
  • // shared_ptr aliasing / shared_ptr 别名: auto shared1 = std::make_shared(42);
  • auto shared2 = shared1; // Both own the data
  • auto shared2 = shared1; // Both own the data / 两者都拥有数据
  • // Who “really” owns it? Neither. Ref count overhead everywhere.
  • // Who “really” owns it? Neither. Ref count overhead everywhere. / 谁是“真正”的所有者?都不是。引用计数的开销到处都是。

}

#![allow(unused)]
fn main() {
// Rust - Ownership system prevents these issues
+// Rust - 所有权系统防止了这些问题
fn rust_ownership_safety() {
-    let data = Box::new(42);  // data owns the heap allocation
+    let data = Box::new(42);  // data owns the heap allocation / data 拥有堆分配的所有权
    
-    let moved_data = data;    // Ownership transferred to moved_data
+    let moved_data = data;    // Ownership transferred / 所有权转移给 moved_data
-    // data is no longer accessible - compile error if used
+    // data is no longer accessible / data 不再可访问 —— 如果使用会导致编译错误
    
-    let borrowed = &moved_data;  // Immutable borrow
+    let borrowed = &moved_data;  // Immutable borrow / 不可变借用
-    println!("{}", borrowed);    // Safe to use
+    println!("{}", borrowed);    // Safe to use / 安全使用
    
-    // moved_data automatically freed when it goes out of scope
+    // moved_data automatically freed when it goes out of scope / moved_data 在离开作用域时自动释放
}
}
graph TD
-    subgraph "C/C++ Memory Management Issues"
+    subgraph "C/C++ Memory Management Issues / 内存管理问题"
-        CP1["int* ptr1"] --> CM["Heap Memory<br/>value: 42"]
+        CP1["int* ptr1 / 指针"] --> CM["Heap Memory / 堆内存<br/>value: 42"]
-        CP2["int* ptr2"] --> CM
+        CP2["int* ptr2 / 指针"] --> CM
-        CP3["int* ptr3"] --> CM
+        CP3["int* ptr3 / 指针"] --> CM
-        CF["free(ptr1)"] --> CM_F["[ERROR] Freed Memory"]
+        CF["free(ptr1) / 释放"] --> CM_F["[ERROR] Freed Memory / 已释放内存"]
-        CP2 -.->|"Use after free<br/>Undefined Behavior"| CM_F
+        CP2 -.->|"Use after free / 释放后使用<br/>Undefined Behavior / 未定义行为"| CM_F
-        CP3 -.->|"Use after free<br/>Undefined Behavior"| CM_F
+        CP3 -.->|"Use after free / 释放后使用<br/>Undefined Behavior / 未定义行为"| CM_F
     end
     
-    subgraph "Rust Ownership System"
+    subgraph "Rust Ownership System / 所有权系统"
-        RO1["data: Box<i32>"] --> RM["Heap Memory<br/>value: 42"]
+        RO1["data: Box<i32> / 所有者"] --> RM["Heap Memory / 堆内存<br/>value: 42"]
-        RO1 -.->|"Move ownership"| RO2["moved_data: Box<i32>"]
+        RO1 -.->|"Move ownership / 移动所有权"| RO2["moved_data: Box<i32> / 所有者"]
         RO2 --> RM
-        RO1_X["data: [WARNING] MOVED<br/>Cannot access"]
+        RO1_X["data: [WARNING] MOVED / 已移动<br/>Cannot access / 无法访问"]
-        RB["&moved_data<br/>Immutable borrow"] -.->|"Safe reference"| RM
+        RB["&moved_data / 借用<br/>Immutable borrow / 不可变"] -.->|"Safe reference / 安全引用"| RM
-        RD["Drop automatically<br/>when out of scope"] --> RM
+        RD["Drop automatically / 自动释放<br/>when out of scope / 离开作用域时"] --> RM
     end
     
     style CM_F fill:#ff6b6b,color:#000
     style CP2 fill:#ff6b6b,color:#000
     style CP3 fill:#ff6b6b,color:#000
     style RO1_X fill:#ffa07a,color:#000
     style RO2 fill:#51cf66,color:#000
     style RB fill:#91e5a3,color:#000
     style RD fill:#91e5a3,color:#000
#![allow(unused)]
fn main() {
fn borrowing_rules_example() {
    let mut data = vec![1, 2, 3, 4, 5];
    
-    // Multiple immutable borrows - OK
+    // Multiple immutable borrows - OK / 多个不可变借用 —— OK
    let ref1 = &data;
    let ref2 = &data;
-    println!("{:?} {:?}", ref1, ref2);  // Both can be used
+    println!("{:?} {:?}", ref1, ref2);  // Both can be used / 两者都可以使用
    
-    // Mutable borrow - exclusive access
+    // Mutable borrow - exclusive access / 可变借用 —— 排他性访问
    let ref_mut = &mut data;
    ref_mut.push(6);
-    // ref1 and ref2 can't be used while ref_mut is active
+    // ref1 and ref2 can't be used while ref_mut is active / 当 ref_mut 处于活跃状态时,不能使用 ref1 和 ref2
    
-    // After ref_mut is done, immutable borrows work again
+    // After ref_mut is done, immutable borrows work again / ref_mut 结束后,不可变借用再次生效
    let ref3 = &data;
    println!("{:?}", ref3);
}
}
graph TD
-    subgraph "Rust Borrowing Rules"
+    subgraph "Rust Borrowing Rules / Rust 借用规则"
         D["mut data: Vec<i32>"]
         
-        subgraph "Phase 1: Multiple Immutable Borrows [OK]"
+        subgraph "Phase 1: Multiple Immutable Borrows [OK] / 阶段 1:多个不可变借用"
             IR1["&data (ref1)"]
             IR2["&data (ref2)"]
             D --> IR1
             D --> IR2
-            IR1 -.->|"Read-only access"| MEM1["Memory: [1,2,3,4,5]"]
+            IR1 -.->|"Read-only access / 只读访问"| MEM1["Memory / 内存: [1,2,3,4,5]"]
-            IR2 -.->|"Read-only access"| MEM1
+            IR2 -.->|"Read-only access / 只读访问"| MEM1
         end
         
-        subgraph "Phase 2: Exclusive Mutable Borrow [OK]"
+        subgraph "Phase 2: Exclusive Mutable Borrow [OK] / 阶段 2:排他性可变借用"
             MR["&mut data (ref_mut)"]
             D --> MR
-            MR -.->|"Exclusive read/write"| MEM2["Memory: [1,2,3,4,5,6]"]
+            MR -.->|"Exclusive read/write / 排他性读写"| MEM2["Memory / 内存: [1,2,3,4,5,6]"]
-            BLOCK["[ERROR] Other borrows blocked"]
+            BLOCK["[ERROR] Other borrows blocked / 其他借用被阻塞"]
         end
         
-        subgraph "Phase 3: Immutable Borrows Again [OK]"
+        subgraph "Phase 3: Immutable Borrows Again [OK] / 阶段 3:再次允许不可变借用"
             IR3["&data (ref3)"]
             D --> IR3
-            IR3 -.->|"Read-only access"| MEM3["Memory: [1,2,3,4,5,6]"]
+            IR3 -.->|"Read-only access / 只读访问"| MEM3["Memory / 内存: [1,2,3,4,5,6]"]
         end
     end
     
-    subgraph "What C/C++ Allows (Dangerous)"
+    subgraph "What C/C++ Allows (Dangerous) / C/C++ 允许的行为(危险)"
         CP["int* ptr"]
         CP2["int* ptr2"]
         CP3["int* ptr3"]
-        CP --> CMEM["Same Memory"]
+        CP --> CMEM["Same Memory / 同一内存"]
         CP2 --> CMEM
         CP3 --> CMEM
-        RACE["[ERROR] Data races possible<br/>[ERROR] Use after free possible"]
+        RACE["[ERROR] Data races possible / 存在数据竞争可能<br/>[ERROR] Use after free possible / 存在释放后使用可能"]
     end
     
     style MEM1 fill:#91e5a3,color:#000
     style MEM2 fill:#91e5a3,color:#000
     style MEM3 fill:#91e5a3,color:#000
     style BLOCK fill:#ffa07a,color:#000
     style RACE fill:#ff6b6b,color:#000
     style CMEM fill:#ff6b6b,color:#000

  • Recall that by default variables are immutable in Rust. Sometimes it’s desirable to have most of a type read-only while permitting write access to a single field.
  • 回想一下,在 Rust 中,变量默认是不可变的。有时我们希望该类型的其余部分都是只读的,但允许修改单个字段。
#![allow(unused)]
fn main() {
struct Employee {
-    employee_id : u64,   // This must be immutable
+    employee_id : u64,   // This must be immutable / 这里必须是不可变的
-    on_vacation: bool,   // What if we wanted to permit write-access to this field, but make employee_id immutable?
+    on_vacation: bool,   // 如果我们想允许修改此字段,但保持 employee_id 不可变呢?
}
}
    • Recall that Rust permits a single mutable reference to a variable and any number of immutable references — enforced at compile-time
    • 回想一下,Rust 允许一个变量有一个可变引用或任意数量的不可变引用 —— 这是在编译时强制执行的
    • What if we wanted to pass an immutable vector of employees, but allow the on_vacation field to be updated, while ensuring employee_id cannot be mutated?
    • 如果我们想传递一个不可变的员工 vector,但是允许更新 on_vacation 字段,同时确保 employee_id 不能被修改呢?
    • Cell<T> provides interior mutability, i.e., write access to specific elements of references that are otherwise read-only
    • Cell<T> 提供内部可变性(interior mutability),即允许修改原本只读的引用的特定元素
    • Works by copying values in and out (requires T: Copy for .get())
    • 通过拷入和拷出值来工作(对于 .get(),需要 T: Copy
    • RefCell<T> provides a variation that works with references
    • RefCell<T> 提供了一种适用于引用的变体
  • - Enforces Rust borrow-checks at **runtime** instead of compile-time
    
  • - 在**运行时**而非编译时强制执行 Rust 的借用检查
    
  • - Allows a single *mutable* borrow, but **panics** if there are any other references outstanding
    
  • - 允许单个*可变*借用,但如果有任何其他引用存在,则会 **panic**
    
  • - Use `.borrow()` for immutable access and `.borrow_mut()` for mutable access
    
  • - 使用 `.borrow()` 进行不可变访问,使用 `.borrow_mut()` 进行可变访问
    

-| Criterion | Cell<T> | RefCell<T> | +| Criterion / 标准 | Cell<T> | RefCell<T> | |———–|———–|———––| -| Works with | Copy types (integers, bools, floats) | Any type (String, Vec, structs) | +| Works with / 适用场景 | Copy types (integers, bools, floats) / Copy 类型 | Any type (String, Vec, structs) / 任何类型 | -| Access pattern | Copies values in/out (.get(), .set()) | Borrows in place (.borrow(), .borrow_mut()) | +| Access pattern / 访问模式 | Copies values in/out / 拷入/拷出值 | Borrows in place / 现场借用 | -| Failure mode | Cannot fail — no runtime checks | Panics if you borrow mutably while another borrow is active | +| Failure mode / 失败模式 | Cannot fail / 不会失败 (no runtime checks) | Panics / 会 Panic(如果已有其他借用时再次尝试可变借用) | -| Overhead | Zero — just copies bytes | Small — tracks borrow state at runtime | +| Overhead / 开销 | Zero / 零 (just copies bytes) | Small / 微小 (tracks borrow state at runtime) | -| Use when | You need a mutable flag, counter, or small value inside an immutable struct | You need to mutate a String, Vec, or complex type inside an immutable struct | +| Use when / 何时使用 | You need a mutable flag, counter… / 需要不可变结构体内的可变标志或计数器 | You need to mutate complex types… / 需要修改不可变结构体内的复杂类型 |


  • Rc<T> allows reference-counted shared ownership of immutable data. What if we wanted to store the same Employee in multiple places without copying?
  • Rc<T> 允许对不可变数据进行引用计数共享所有权。如果我们想在多个地方存储同一个 Employee 而不进行拷贝呢?
#[derive(Debug)]
struct Employee {
    employee_id: u64,
}
fn main() {
    let mut us_employees = vec![];
    let mut all_global_employees = Vec::<Employee>::new();
    let employee = Employee { employee_id: 42 };
    us_employees.push(employee);
-    // Won't compile — employee was already moved
+    // Won't compile — employee was already moved / 无法编译 —— employee 已被移动
    //all_global_employees.push(employee);
}
  • Rc<T> solves the problem by allowing shared immutable access:
  • Rc<T> 通过允许共享的不可变访问来解决此问题:
    • The contained type is automatically dereferenced
    • 包含的类型会被自动解引用
    • The type is dropped when the reference count goes to 0
    • 当引用计数归零时,该类型会被释放(drop)
use std::rc::Rc;
#[derive(Debug)]
struct Employee {employee_id: u64}
fn main() {
    let mut us_employees = vec![];
    let mut all_global_employees = vec![];
    let employee = Employee { employee_id: 42 };
    let employee_rc = Rc::new(employee);
-    us_employees.push(employee_rc.clone());
+    us_employees.push(employee_rc.clone()); // 克隆 Rc 句柄
-    all_global_employees.push(employee_rc.clone());
+    all_global_employees.push(employee_rc.clone()); // 克隆 Rc 句柄
-    let employee_one = all_global_employees.get(0); // Shared immutable reference
+    let employee_one = all_global_employees.get(0); // Shared immutable reference / 共享的不可变引用
    for e in us_employees {
-        println!("{}", e.employee_id);  // Shared immutable reference
+        println!("{}", e.employee_id);  // Shared immutable reference / 共享的不可变引用
    }
    println!("{employee_one:?}");
}
  • For C++ developers: Smart Pointer Mapping

  • For C++ developers: Smart Pointer Mapping / C++ 开发者:智能指针映射

-| C++ Smart Pointer | Rust Equivalent | Key Difference | +| C++ Smart Pointer / C++ 智能指针 | Rust Equivalent / Rust 等等价物 | Key Difference / 关键区别 | |—|—|—| -| std::unique_ptr<T> | Box<T> | Rust’s version is the default — move is language-level, not opt-in | +| std::unique_ptr<T> | Box<T> | Rust’s version is the default / Rust 的版本是默认行为 —— 移动是语言层面的 | -| std::shared_ptr<T> | Rc<T> (single-thread) / Arc<T> (multi-thread) | No atomic overhead for Rc; use Arc only when sharing across threads | +| std::shared_ptr<T> | Rc<T> (single-thread) / Arc<T> (multi-thread) | No atomic overhead for Rc / Rc 没有任何原子操作开销;仅在跨线程共享时才使用 Arc | -| std::weak_ptr<T> | Weak<T> (from Rc::downgrade() or Arc::downgrade()) | Same purpose: break reference cycles | +| std::weak_ptr<T> | Weak<T> | Same purpose: break reference cycles / 目的相同:打破引用循环 |

  • Key distinction: In C++, you choose to use smart pointers. In Rust, owned values (T) and borrowing (&T) cover most use cases — reach for Box/Rc/Arc only when you need heap allocation or shared ownership.

  • Key distinction / 关键区别:在 C++ 中,你选择去使用智能指针。在 Rust 中,拥有所有权的值(T)和借用(&T)涵盖了大多数用例 —— 只有当你确实需要堆分配或共享所有权时,才会求助于 Box/Rc/Arc

  • Rc<T> uses reference counting — if two Rc values point to each other, neither will ever be dropped (a cycle). Weak<T> solves this:
  • Rc<T> 使用引用计数 —— 如果两个 Rc 值互相指向对方,它们都永远不会被释放(产生循环)。Weak<T> 解决了这个问题:
use std::rc::{Rc, Weak};

struct Node {
    value: i32,
-    parent: Option<Weak<Node>>,  // Weak reference — doesn't prevent drop
+    parent: Option<Weak<Node>>,  // Weak reference / 弱引用 —— 不会阻止被释放
}

fn main() {
    let parent = Rc::new(Node { value: 1, parent: None });
    let child = Rc::new(Node {
        value: 2,
-        parent: Some(Rc::downgrade(&parent)),  // Weak ref to parent
+        parent: Some(Rc::downgrade(&parent)),  // Weak ref to parent / 对父节点的弱引用
    });

-    // To use a Weak, try to upgrade it — returns Option<Rc<T>>
+    // To use a Weak, try to upgrade it — returns Option<Rc<T>> / 要使用 Weak,尝试通过 upgrade 提升它 —— 返回 Option<Rc<T>>
    if let Some(parent_rc) = child.parent.as_ref().unwrap().upgrade() {
        println!("Parent value: {}", parent_rc.value);
    }
-    println!("Parent strong count: {}", Rc::strong_count(&parent)); // 1, not 2
+    println!("Parent strong count: {}", Rc::strong_count(&parent)); // 1, not 2 / 强引用计数为 1,而不是 2
}
  • Weak<T> is covered in more depth in Avoiding Excessive clone(). For now, the key takeaway: use Weak for “back-references” in tree/graph structures to avoid memory leaks.


  • The real power emerges when you combine Rc<T> (shared ownership) with Cell<T> or RefCell<T> (interior mutability). This lets multiple owners read and modify shared data:
  • 当你将 Rc<T>(共享所有权)与 Cell<T>RefCell<T>(内部可变性)结合使用时,真正的威力就显现出来了。这允许多个所有者读取和修改共享数据:

-| Pattern | Use case | +| Pattern / 模式 | Use case / 用例 | |———|–––––| -| Rc<RefCell<T>> | Shared, mutable data (single-threaded) | +| Rc<RefCell<T>> | Shared, mutable data / 共享、可变数据(单线程) | -| Arc<Mutex<T>> | Shared, mutable data (multi-threaded — see ch13) | +| Arc<Mutex<T>> | Shared, mutable data / 共享、可变数据(多线程 —— 见 第13章) | -| Rc<Cell<T>> | Shared, mutable Copy types (simple flags, counters) | +| Rc<Cell<T>> | Shared, mutable Copy types / 共享、可变 Copy 类型(简单标志、计数器) |


  • 🟡 Intermediate
  • 🟡 Intermediate / 中级
    • Part 1 (Rc): Create an Employee struct with employee_id: u64 and name: String. Place it in an Rc<Employee> and clone it into two separate Vecs (us_employees and global_employees). Print from both vectors to show they share the same data.
    • Part 1 (Rc): Create an Employee struct with employee_id: u64 and name: String. Place it in an Rc<Employee> and clone it into two separate Vecs (us_employees and global_employees). Print from both vectors to show they share the same data. / 第一部分 (Rc):创建一个包含 employee_id: u64name: StringEmployee 结构体。将其放入 Rc<Employee> 中并克隆到两个不同的 Vec 中(us_employeesglobal_employees)。从两个向量中打印数据,以证明它们共享相同的数据。
    • Part 2 (Cell): Add an on_vacation: Cell<bool> field to Employee. Pass an immutable &Employee reference to a function and toggle on_vacation from inside that function — without making the reference mutable.
    • Part 2 (Cell): Add an on_vacation: Cell<bool> field to Employee. Pass an immutable &Employee reference to a function and toggle on_vacation from inside that function — without making the reference mutable. / 第二部分 (Cell):为 Employee 添加一个 on_vacation: Cell<bool> 字段。将不可变的 &Employee 引用传递给一个函数,并在函数内部切换 on_vacation 状态 —— 而无需将引用变为可变的。
    • Part 3 (RefCell): Replace name: String with name: RefCell<String> and write a function that appends a suffix to the employee’s name through an &Employee (immutable reference).
    • Part 3 (RefCell): Replace name: String with name: RefCell<String> and write a function that appends a suffix to the employee’s name through an &Employee (immutable reference). / 第三部分 (RefCell):将 name: String 替换为 name: RefCell<String>,并编写一个函数,通过 &Employee(不可变引用)在员工姓名后追加后缀。
  • Starter code:
  • Starter code / 初始代码:
use std::cell::{Cell, RefCell};
use std::rc::Rc;

#[derive(Debug)]
struct Employee {
    employee_id: u64,
    name: RefCell<String>,
    on_vacation: Cell<bool>,
}

fn toggle_vacation(emp: &Employee) {
-    // TODO: Flip on_vacation using Cell::set()
+    // TODO: Flip on_vacation using Cell::set() / TODO:使用 Cell::set() 翻转 on_vacation
}

fn append_title(emp: &Employee, title: &str) {
-    // TODO: Borrow name mutably via RefCell and push_str the title
+    // TODO: Borrow name mutably via RefCell and push_str the title / TODO:通过 RefCell 可变借用姓名并追加标题
}

fn main() {
-    // TODO: Create an employee, wrap in Rc, clone into two Vecs,
+    // TODO: Create an employee, wrap in Rc, clone into two Vecs, / TODO:创建一个员工,包装在 Rc 中,克隆到两个 Vec 中,
-    // call toggle_vacation and append_title, print results
+    // 调用 toggle_vacation 和 append_title,打印结果
}
  • Solution (click to expand)
  • Solution (click to expand) / 解决方案(点击展开)
use std::cell::{Cell, RefCell};
use std::rc::Rc;

#[derive(Debug)]
struct Employee {
    employee_id: u64,
    name: RefCell<String>,
    on_vacation: Cell<bool>,
}

fn toggle_vacation(emp: &Employee) {
    emp.on_vacation.set(!emp.on_vacation.get());
}

fn append_title(emp: &Employee, title: &str) {
    emp.name.borrow_mut().push_str(title);
}

fn main() {
    let emp = Rc::new(Employee {
        employee_id: 42,
        name: RefCell::new("Alice".to_string()),
        on_vacation: Cell::new(false),
    });

    let mut us_employees = vec![];
    let mut global_employees = vec![];
    us_employees.push(Rc::clone(&emp));
    global_employees.push(Rc::clone(&emp));

-    // Toggle vacation through an immutable reference
+    // Toggle vacation through an immutable reference / 通过不可变引用切换休假状态
    toggle_vacation(&emp);
    println!("On vacation: {}", emp.on_vacation.get()); // true

-    // Append title through an immutable reference
+    // Append title through an immutable reference / 通过不可变引用追加标题
    append_title(&emp, ", Sr. Engineer");
    println!("Name: {}", emp.name.borrow()); // "Alice, Sr. Engineer"

-    // Both Vecs see the same data (Rc shares ownership)
+    // Both Vecs see the same data (Rc shares ownership) / 两个 Vec 都看到相同的数据(Rc 共享所有权)
    println!("US: {:?}", us_employees[0].name.borrow());
    println!("Global: {:?}", global_employees[0].name.borrow());
    println!("Rc strong count: {}", Rc::strong_count(&emp));
}
- // Output:
+ // Output / 输出:
// On vacation: true
// Name: Alice, Sr. Engineer
// US: "Alice, Sr. Engineer"
// Global: "Alice, Sr. Engineer"
// Rc strong count: 3

Rust crates and modules / Rust Crate 与模块

What you’ll learn / 你将学到: How Rust organizes code into modules and crates — privacy-by-default visibility, pub modifiers, workspaces, and the crates.io ecosystem. Replaces C/C++ header files, #include, and CMake dependency management.

Rust 如何将代码组织成模块和 crate —— 默认私有的可见性、pub 修饰符、工作空间以及 crates.io 生态系统。这些将取代 C/C++ 的头文件、#include 和 CMake 依赖管理。

  • Modules are the fundamental organizational unit of code within crates / 模块是 crate 内部基本的代码组织单元
  • - Each source file (.rs) is its own module, and can create nested modules using the ```mod``` keyword.
    
  • - Each source file (.rs) is its own module, and can create nested modules using the ```mod``` keyword. / 每个源文件 (.rs) 都是一个独立的模块,并可以使用 ```mod``` 关键字创建嵌套模块。
    
  • - All types in a (sub-) module are **private** by default, and aren't externally visible within the same crate unless they are explicitly marked as ```pub``` (public). The scope of ```pub``` can be further restricted to ```pub(crate)```, etc
    
  • - All types in a (sub-) module are **private** by default, and aren't externally visible within the same crate unless they are explicitly marked as ```pub``` (public). The scope of ```pub``` can be further restricted to ```pub(crate)```, etc / (子)模块中的所有类型默认都是**私有的**,除非显式标记为 ```pub``` (public),否则在同一个 crate 内部也是外部不可见的。```pub``` 的范围可以进一步限制为 ```pub(crate)``` 等。
    
  • - Even if an type is public, it doesn't automatically become visible within the scope of another module unless it's imported using the ```use``` keyword. Child submodules can reference types in the parent scope using the ```use super::```
    
  • - Even if an type is public, it doesn't automatically become visible within the scope of another module unless it's imported using the ```use``` keyword. Child submodules can reference types in the parent scope using the ```use super::``` / 即使一个类型是公有的,它也不会自动在另一个模块的作用域内可见,除非使用 ```use``` 关键字导入。子模块可以使用 ```use super::``` 引用父级作用域中的类型。
    
  • - Source files (.rs) aren't automatically included in the crate **unless** they are explicitly listed in ```main.rs``` (executable) or ```lib.rs```
    
  • - Source files (.rs) aren't automatically included in the crate **unless** they are explicitly listed in ```main.rs``` (executable) or ```lib.rs``` / 源文件 (.rs) 不会自动包含在 crate 中,**除非**它们在 ```main.rs```(可执行文件)或 ```lib.rs```(库)中被显式列出。
    
    • We’ll take a look at modifying our hello world to call another function
    • We’ll take a look at modifying our hello world to call another function / 我们来看看如何修改 hello world 示例以调用另一个函数
  • - As previously mentioned, function are defined with the ```fn``` keyword. The ```->``` keyword declares that the function returns a value (the default is void) with the type ```u32``` (unsigned 32-bit integer)
    
  • - As previously mentioned, function are defined with the ```fn``` keyword. The ```->``` keyword declares that the function returns a value (the default is void) with the type ```u32``` (unsigned 32-bit integer) / 如前所述,函数使用 ```fn``` 关键字定义。```->``` 关键字声明函数返回一个值(默认为 void),类型为 ```u32```(32 位无符号整数)。
    
  • - Functions are scoped by module, i.e., two functions with exact same name in two modules won't have a name collision
    
  • - Functions are scoped by module, i.e., two functions with exact same name in two modules won't have a name collision / 函数受模块作用域限制,即两个不同模块中同名的两个函数不会发生命名冲突。
    
  •     - The module scoping extends to all types (for example, a ```struct foo``` in ```mod a { struct foo; }``` is a distinct type (```a::foo```) from ```mod b { struct foo; }``` (```b::foo```))
    
  •     - The module scoping extends to all types (for example, a ```struct foo``` in ```mod a { struct foo; }``` is a distinct type (```a::foo```) from ```mod b { struct foo; }``` (```b::foo```)) / 模块作用域适用于所有类型(例如,```mod a { struct foo; }``` 中的 ```struct foo``` 与 ```mod b { struct foo; }``` 中的 ```b::foo``` 是不同的类型)。
    
  • Starter code — complete the functions:
  • Starter code / 初始代码 —— 完成以下函数:
mod math {
-    // TODO: implement pub fn add(a: u32, b: u32) -> u32
+    // TODO: implement pub fn add(a: u32, b: u32) -> u32 / TODO:实现 pub fn add(a: u32, b: u32) -> u32
}

fn greet(name: &str) -> String {
-    // TODO: return "Hello, <name>! The secret number is <math::add(21,21)>"
+    // TODO: return "Hello, <name>! The secret number is <math::add(21,21)>" / TODO:返回 "Hello, <name>! The secret number is <math::add(21,21)>"
    todo!()
}

fn main() {
    println!("{}", greet("Rustacean"));
}
  • Solution (click to expand)
  • Solution (click to expand) / 解决方案(点击展开)
mod math {
    pub fn add(a: u32, b: u32) -> u32 {
        a + b
    }
}

fn greet(name: &str) -> String {
    format!("Hello, {}! The secret number is {}", name, math::add(21, 21))
}

fn main() {
    println!("{}", greet("Rustacean"));
}
-// Output: Hello, Rustacean! The secret number is 42
+// Output / 输出:Hello, Rustacean! The secret number is 42
    • Any significant Rust project should use workspaces to organize component crates
    • Any significant Rust project should use workspaces to organize component crates / 任何具有一定规模的 Rust 项目都应使用工作空间来组织各组件 crate
  • - A workspace is simply a collection of local crates that will be used to build the target binaries. The `Cargo.toml` at the workspace root should have a pointer to the constituent packages (crates)
    
  • - A workspace is simply a collection of local crates that will be used to build the target binaries. The `Cargo.toml` at the workspace root should have a pointer to the constituent packages (crates) / 工作空间仅仅是用于构建目标二进制文件的本地 crate 集合。工作空间根目录下的 `Cargo.toml` 应包含指向各组成包(crate)的指针。
    
[workspace]
resolver = "2"
members = ["package1", "package2"]
- workspace_root/
+ workspace_root/ # 工作空间根目录
- |-- Cargo.toml      # Workspace configuration
+ |-- Cargo.toml      # Workspace configuration / 工作空间配置
|-- package1/
- |   |-- Cargo.toml  # Package 1 configuration
+ |   |-- Cargo.toml  # Package 1 configuration / 包 1 配置
|   `-- src/
- |       `-- lib.rs  # Package 1 source code
+ |       `-- lib.rs  # Package 1 source code / 包 1 源码
|-- package2/
- |   |-- Cargo.toml  # Package 2 configuration
+ |   |-- Cargo.toml  # Package 2 configuration / 包 2 配置
|   `-- src/
- |       `-- main.rs # Package 2 source code
+ |       `-- main.rs # Package 2 source code / 包 2 源码

    • We’ll create a simple package and use it from our hello world program`
    • We’ll create a simple package and use it from our hello world program / 我们将创建一个简单的包,并在我们的 hello world 程序中使用它
    • Create the workspace directory
    • Create the workspace directory / 创建工作空间目录
mkdir workspace
cd workspace
    • Create a file called Cargo.toml and add the following to it. This creates an empty workspace
    • Create a file called Cargo.toml and add the following to it. This creates an empty workspace / 创建一个名为 Cargo.toml 的文件并添加以下内容。这将创建一个空的工作空间
[workspace]
resolver = "2"
members = []
    • Add the packages (cargo new --lib specifies a library instead of an executable`)
    • Add the packages (cargo new --lib specifies a library instead of an executable) / 添加包(cargo new --lib 指定创建一个库而不是可执行文件)
cargo new hello
cargo new --lib hellolib
    • Take a look at the generated Cargo.toml in hello and hellolib. Notice that both of them have been to the upper level Cargo.toml
    • Take a look at the generated Cargo.toml in hello and hellolib. Notice that both of them have been to the upper level Cargo.toml / 查看 hellohellolib 中生成的 Cargo.toml。注意它们都已被添加到上一级的 Cargo.toml 中。
    • The presence of lib.rs in hellolib implies a library package (see https://doc.rust-lang.org/cargo/reference/cargo-targets.html for customization options)
    • The presence of lib.rs in hellolib implies a library package (see https://doc.rust-lang.org/cargo/reference/cargo-targets.html for customization options) / helloliblib.rs 的存在意味着这是一个库包(有关自定义选项,请参阅 https://doc.rust-lang.org/cargo/reference/cargo-targets.html)。
    • Adding a dependency on hellolib in Cargo.toml for hello
    • Adding a dependency on hellolib in Cargo.toml for hello / 在 helloCargo.toml 中添加对 hellolib 的依赖:
[dependencies]
hellolib = {path = "../hellolib"}
    • Using add() from hellolib
    • Using add() from hellolib / 从 hellolib 中使用 add()
fn main() {
    println!("Hello, world! {}", hellolib::add(21, 21));
}
  • Solution (click to expand)
  • Solution (click to expand) / 解决方案(点击展开)
  • The complete workspace setup:
  • 完整的工作空间设置:
- # Terminal commands
+ # Terminal commands / 终端命令
mkdir workspace && cd workspace

- # Create workspace Cargo.toml
+ # Create workspace Cargo.toml / 创建工作空间 Cargo.toml
cat > Cargo.toml << 'EOF'
[workspace]
resolver = "2"
members = ["hello", "hellolib"]
EOF

cargo new hello
cargo new --lib hellolib
- # hello/Cargo.toml — add dependency
+ # hello/Cargo.toml — add dependency / 添加依赖
[dependencies]
hellolib = {path = "../hellolib"}
#![allow(unused)]
fn main() {
- // hellolib/src/lib.rs — already has add() from cargo new --lib
+ // hellolib/src/lib.rs — already has add() / 已经带有 add()
pub fn add(left: u64, right: u64) -> u64 {
    left + right
}
}
// hello/src/main.rs
fn main() {
    println!("Hello, world! {}", hellolib::add(21, 21));
}
-// Output: Hello, world! 42
+// Output / 输出:Hello, world! 42
    • Rust has a vibrant ecosystem of community crates (see https://crates.io/)
    • Rust has a vibrant ecosystem of community crates (see https://crates.io/) / Rust 拥有充满活力的社区 crate 生态系统(见 https://crates.io/)
  • - The Rust philosophy is to keep the standard library compact and outsource functionality to community crates
    
  • - The Rust philosophy is to keep the standard library compact and outsource functionality to community crates / Rust 的哲学是保持标准库精简,并将功能外包给社区 crate。
    
  • - There is no hard and fast rule about using community crates, but the rule of thumb should be ensure that the crate has a decent maturity level (indicated by the version number), and that it's being actively maintained. Reach out to internal sources if in doubt about a crate
    
  • - There is no hard and fast rule about using community crates, but the rule of thumb should be ensure that the crate has a decent maturity level (indicated by the version number), and that it's being actively maintained. Reach out to internal sources if in doubt about a crate / 关于使用社区 crate 没有一成不变的规则,但经验法则是应确保该 crate 具有适当的成熟度(由版本号指示),并且正在积极维护。如果对某个 crate 有疑问,请咨询内部资源。
    
    • Every crate published on crates.io has a major and minor version
    • Every crate published on crates.io has a major and minor version / 在 crates.io 上发布的每个 crate 都包含主版本号和次版本号。
  • - Crates are expected to observe the major and minor ```SemVer``` guidelines defined here: https://doc.rust-lang.org/cargo/reference/semver.html
    
  • - Crates are expected to observe the major and minor ```SemVer``` guidelines defined here: https://doc.rust-lang.org/cargo/reference/semver.html / Crate 应遵守此处定义的 ```SemVer```(语义化版本控制)指南:https://doc.rust-lang.org/cargo/reference/semver.html。
    
  • - The TL;DR version is that there should be no breaking changes for the same minor version. For example, v0.11 must be compatible with v0.15 (but v0.20 may have breaking changes)
    
  • - The TL;DR version is that there should be no breaking changes for the same minor version. For example, v0.11 must be compatible with v0.15 (but v0.20 may have breaking changes) / 简单来说,在同一个次版本内不应有破坏性更改。例如,v0.11 必须与 v0.15 兼容(但 v0.20 可能会有破坏性更改)。
    
    • Crates can define dependencies on a specific versions of a crate, specific minor or major version, or don’t care. The following examples show the Cargo.toml entries for declaring a dependency on the rand crate
    • Crates can define dependencies on a specific versions of a crate, specific minor or major version, or don’t care. The following examples show the Cargo.toml entries for declaring a dependency on the rand crate / Crate 可以定义对特定版本、特定次版本、主版本或任意版本的依赖。以下示例显示了在 Cargo.toml 中声明对 rand crate 依赖的条目。
    • At least 0.10.0, but anything < 0.11.0 is fine
    • At least 0.10.0, but anything < 0.11.0 is fine / 至少 0.10.0,但任何 < 0.11.0 的版本都可以
[dependencies]
rand = { version = "0.10.0"}
    • Only 0.10.0, and nothing else
    • Only 0.10.0, and nothing else / 仅限 0.10.0,别无他选
[dependencies]
rand = { version = "=0.10.0"}
    • Don’t care; cargo will select the latest version
    • Don’t care; cargo will select the latest version / 无所谓;cargo 将选择最新版本
[dependencies]
rand = { version = "*"}
    • Reference: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html
    • Reference / 参考:https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html

    • Modify the helloworld example to print a random number
    • Modify the helloworld example to print a random number / 修改 helloworld 示例以打印一个随机数
    • Use cargo add rand to add a dependency
    • Use cargo add rand to add a dependency / 使用 cargo add rand 添加依赖
    • Use https://docs.rs/rand/latest/rand/ as a reference for the API
    • Use https://docs.rs/rand/latest/rand/ as a reference for the API / 以 https://docs.rs/rand/latest/rand/ 作为 API 参考
  • Starter code — add this to main.rs after running cargo add rand:
  • Starter code / 初始代码 —— 在运行 cargo add rand 后,将以下内容添加到 main.rs
use rand::RngExt;

fn main() {
    let mut rng = rand::rng();
-    // TODO: Generate and print a random u32 in 1..=100
+    // TODO: Generate and print a random u32 in 1..=100 / TODO:生成并打印 1..=100 之间的随机 u32
-    // TODO: Generate and print a random bool
+    // TODO: Generate and print a random bool / TODO:生成并打印随机布尔值
-    // TODO: Generate and print a random f64
+    // TODO: Generate and print a random f64 / TODO:生成并打印随机 f64
}
  • Solution (click to expand)
  • Solution (click to expand) / 解决方案(点击展开)
use rand::RngExt;

fn main() {
    let mut rng = rand::rng();
    let n: u32 = rng.random_range(1..=100);
    println!("Random number (1-100): {n}");

-    // Generate a random boolean
+    // Generate a random boolean / 生成随机布尔值
    let b: bool = rng.random();
    println!("Random bool: {b}");

-    // Generate a random float between 0.0 and 1.0
+    // Generate a random float between 0.0 and 1.0 / 生成 0.0 到 1.0 之间的随机浮点数
    let f: f64 = rng.random();
    println!("Random float: {f:.4}");
}
    • As mentioned previously, Cargo.lock is automatically generated from Cargo.toml
    • As mentioned previously, Cargo.lock is automatically generated from Cargo.toml / 如前所述,Cargo.lock 是根据 Cargo.toml 自动生成的。
  • - The main idea behind Cargo.lock is to ensure reproducible builds. For example, if ```Cargo.toml``` had specified a version of ```0.10.0```, cargo is free to choose any version that is ```< 0.11.0```
    
  • - The main idea behind Cargo.lock is to ensure reproducible builds. For example, if ```Cargo.toml``` had specified a version of ```0.10.0```, cargo is free to choose any version that is ```< 0.11.0``` / Cargo.lock 的核心思想是确保构建的可复现性。例如,如果 ```Cargo.toml``` 指定了 ```0.10.0``` 版本,cargo 可以自由选择任何 ```< 0.11.0``` 的版本。
    
  • - Cargo.lock contains the *specific* version of the rand crate that was used during the build.
    
  • - Cargo.lock contains the *specific* version of the rand crate that was used during the build. / Cargo.lock 包含了构建期间使用的 rand crate 的*具体*版本。
    
  • - The recommendation is to include ```Cargo.lock``` in the git repo to ensure reproducible builds
    
  • - The recommendation is to include ```Cargo.lock``` in the git repo to ensure reproducible builds / 建议将 ```Cargo.lock``` 包含在 git 仓库中,以确保构建的可复现性。
    
    • Rust unit tests reside in the same source file (by convention), and are usually grouped into separate module
    • Rust unit tests reside in the same source file (by convention), and are usually grouped into separate module / (按照惯例)Rust 单元测试位于同一个源文件中,通常被分在独立的模块内。
  • - The test code is never included in the actual binary. This is made possible by the ```cfg``` (configuration) feature. Configurations are useful for creating platform specific code (```Linux``` vs. ```Windows```) for example
    
  • - The test code is never included in the actual binary. This is made possible by the ```cfg``` (configuration) feature. Configurations are useful for creating platform specific code (```Linux``` vs. ```Windows```) for example / 测试代码永远不会包含在实际的二进制文件中。这是通过 ```cfg```(配置)特性实现的。例如,配置对于创建特定于平台的代码(如 ```Linux``` vs ```Windows```)非常有用。
    
  • - Tests can be executed with ```cargo test```. Reference: https://doc.rust-lang.org/reference/conditional-compilation.html
    
  • - Tests can be executed with ```cargo test```. Reference / 参考:https://doc.rust-lang.org/reference/conditional-compilation.html。
    
#![allow(unused)]
fn main() {
pub fn add(left: u64, right: u64) -> u64 {
    left + right
}
- // Will be included only during testing
+ // Will be included only during testing / 仅在测试期间包含
#[cfg(test)]
mod tests {
-    use super::*; // This makes all types in the parent scope visible
+    use super::*; // This makes all types in the parent scope visible / 使父作用域中的所有类型可见
    #[test]
    fn it_works() {
-        let result = add(2, 2); // Alternatively, super::add(2, 2);
+        let result = add(2, 2); // Alternatively / 或者:super::add(2, 2);
        assert_eq!(result, 4);
    }
}
}
    • cargo has several other useful features including:
    • cargo 还有其他几个有用的功能,包括:
  • - ```cargo clippy``` is a great way of linting Rust code. In general, warnings should be fixed (or rarely suppressed if really warranted)
    
  • - ```cargo clippy``` 是对 Rust 代码进行 lint(静态检查)的绝佳方式。通常,应修复所有警告(或者在确实必要且有根据的情况下极少数地进行抑制)。
    
  • - ```cargo format``` executes the ```rustfmt``` tool to format source code. Using the tool ensures standard formatting of checked-in code and puts an end to debates about style
    
  • - ```cargo format``` 执行 ```rustfmt``` 工具来格式化源代码。使用此工具可确保提交的代码符合标准格式,并终结关于代码风格的争论。
    
  • - ```cargo doc``` can be used to generate documentation from the ```///``` style comments. The documentation for all crates on ```crates.io``` was generated using this method
    
  • - ```cargo doc``` 可用于从 ```///``` 风格的注释生成文档。```crates.io``` 上所有 crate 的文档都是使用此方法生成的。
    
  • In C, you pass -O0, -O2, -Os, -flto to gcc/clang. In Rust, you configure
  • 在 C 中,你将 -O0-O2-Os-flto 传递给 gcc/clang。在 Rust 中,你在 Cargo.toml 中配置构建配置(profiles):
  • build profiles in Cargo.toml:
- # Cargo.toml — build profile configuration
+ # Cargo.toml — build profile configuration / 构建配置

[profile.dev]
- opt-level = 0          # No optimization (fast compile, like -O0)
+ opt-level = 0          # No optimization / 无优化 (fast compile, like -O0)
- debug = true           # Full debug symbols (like -g)
+ debug = true           # Full debug symbols / 完整调试符号 (like -g)

[profile.release]
- opt-level = 3          # Maximum optimization (like -O3)
+ opt-level = 3          # Maximum optimization / 最大优化 (like -O3)
- lto = "fat"            # Link-Time Optimization (like -flto)
+ lto = "fat"            # Link-Time Optimization / 链接时优化 (like -flto)
- strip = true           # Strip symbols (like the strip command)
+ strip = true           # Strip symbols / 剥离符号 (like the strip command)
- codegen-units = 1      # Single codegen unit — slower compile, better optimization
+ codegen-units = 1      # Single codegen unit / 单独的代码生成单元 —— 编译较慢,优化更好
- panic = "abort"        # No unwind tables (smaller binary)
+ panic = "abort"        # No unwind tables / 无展开表 (smaller binary)

-| C/GCC Flag | Cargo.toml Key | Values | +| C/GCC Flag | Cargo.toml Key | Values / 值 | |————|—————|––––| -| -O0 / -O2 / -O3 | opt-level | 0, 1, 2, 3, "s", "z" | +| -O0 / -O2 / -O3 | opt-level | 0, 1, 2, 3, "s"(优化大小), "z"(极致优化大小) | -| -flto | lto | false, "thin", "fat" | +| -flto | lto | false, "thin", "fat" | -| -g / no -g | debug | true, false, "line-tables-only" | +| -g / no -g | debug | true, false, "line-tables-only" | -| strip command | strip | "none", "debuginfo", "symbols", true/false | +| strip command | strip | "none", "debuginfo", "symbols", true/false | -| — | codegen-units | 1 = best opt, slowest compile | +| — | codegen-units | 1 = best opt, slowest compile / 最好优化,最慢编译 |

- cargo build              # Uses [profile.dev]
+ cargo build              # Uses [profile.dev] / 使用开发配置
- cargo build --release    # Uses [profile.release]
+ cargo build --release    # Uses [profile.release] / 使用发布配置
  • In C, you use Makefiles or CMake to link libraries and run code generation.
  • 在 C 中,你使用 Makefiles 或 CMake 来链接库并运行代码生成。
  • Rust uses a build.rs file at the crate root:
  • Rust 在 crate 根目录下使用 build.rs 文件:
- // build.rs — runs before compiling the crate
+ // build.rs — runs before compiling the crate / 在编译 crate 之前运行

fn main() {
-    // Link a system C library (like -lbmc_ipmi in gcc)
+    // Link a system C library (like -lbmc_ipmi in gcc) / 链接系统 C 库(类似于 gcc 中的 -lbmc_ipmi)
    println!("cargo::rustc-link-lib=bmc_ipmi");

-    // Where to find the library (like -L/usr/lib/bmc)
+    // Where to find the library (like -L/usr/lib/bmc) / 到哪里寻找库(类似于 -L/usr/lib/bmc)
    println!("cargo::rustc-link-search=/usr/lib/bmc");

-    // Re-run if the C header changes
+    // Re-run if the C header changes / 如果 C 头文件发生变化则重新运行
    println!("cargo::rerun-if-changed=wrapper.h");
}
  • You can even compile C source files directly from a Rust crate:
  • 你甚至可以直接从 Rust crate 编译 C 源文件:
# Cargo.toml
[build-dependencies]
- cc = "1"  # C compiler integration
+ cc = "1"  # C compiler integration / C 编译器集成
// build.rs
fn main() {
    cc::Build::new()
        .file("src/c_helpers/ipmi_raw.c")
        .include("/usr/include/bmc")
-        .compile("ipmi_raw");   // Produces libipmi_raw.a, linked automatically
+        .compile("ipmi_raw");   // Produces libipmi_raw.a / 产生 libipmi_raw.a,自动链接
    println!("cargo::rerun-if-changed=src/c_helpers/ipmi_raw.c");
}

-| C / Make / CMake | Rust build.rs | +| C / Make / CMake | Rust build.rs | |—————–|—————–| -| -lfoo | println!("cargo::rustc-link-lib=foo") | +| -lfoo | println!("cargo::rustc-link-lib=foo") | -| -L/path | println!("cargo::rustc-link-search=/path") | +| -L/path | println!("cargo::rustc-link-search=/path") | -| Compile C source | cc::Build::new().file("foo.c").compile("foo") | +| Compile C source / 编译 C 源码 | cc::Build::new().file("foo.c").compile("foo") | -| Generate code | Write files to $OUT_DIR, then include!() | +| Generate code / 生成代码 | Write files to $OUT_DIR, then include!() / 将文件写入 $OUT_DIR,然后使用 include!() |

  • In C, cross-compilation requires installing a separate toolchain (arm-linux-gnueabihf-gcc)
  • 在 C 中,交叉编译需要安装一个独立的工具链(arm-linux-gnueabihf-gcc
  • and configuring Make/CMake. In Rust:
  • 并配置 Make/CMake。在 Rust 中:
- # Install a cross-compilation target
+ # Install a cross-compilation target / 安装交叉编译目标
rustup target add aarch64-unknown-linux-gnu

- # Cross-compile
+ # Cross-compile / 交叉编译
cargo build --target aarch64-unknown-linux-gnu --release
  • Specify the linker in .cargo/config.toml:
  • .cargo/config.toml 中指定链接器:
[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"

-| C Cross-Compile | Rust Equivalent | +| C Cross-Compile / C 交叉编译 | Rust Equivalent / Rust 等价物 | |—————–|—————–| -| apt install gcc-aarch64-linux-gnu | rustup target add aarch64-unknown-linux-gnu + install linker | +| apt install gcc-aarch64-linux-gnu | rustup target add aarch64-unknown-linux-gnu + install linker / 安装链接器 | -| CC=aarch64-linux-gnu-gcc make | .cargo/config.toml [target.X] linker = "..." | +| CC=aarch64-linux-gnu-gcc make | .cargo/config.toml [target.X] linker = "..." | -| #ifdef __aarch64__ | #[cfg(target_arch = "aarch64")] | +| #ifdef __aarch64__ | #[cfg(target_arch = "aarch64")] | -| Separate Makefile targets | cargo build --target ... | +| Separate Makefile targets / 独立的 Makefile 目标 | cargo build --target ... |

  • C uses #ifdef and -DFOO for conditional compilation. Rust uses feature flags
  • C 使用 #ifdef-DFOO 进行条件编译。Rust 使用在 Cargo.toml 中定义的 feature 标志:
  • defined in Cargo.toml:
# Cargo.toml
[features]
- default = ["json"]         # Enabled by default
+ default = ["json"]         # Enabled by default / 默认启用
- json = ["dep:serde_json"]  # Optional dependency
+ json = ["dep:serde_json"]  # Optional dependency / 可选依赖
- verbose = []               # Flag with no dependency
+ verbose = []               # Flag with no dependency / 无依赖的标志
- gpu = ["dep:cuda-sys"]     # Optional GPU support
+ gpu = ["dep:cuda-sys"]     # Optional GPU support / 可选的 GPU 支持
#![allow(unused)]
fn main() {
- // Code gated on features:
+ // Code gated on features / 受 feature 控制的代码:
#[cfg(feature = "json")]
pub fn parse_config(data: &str) -> Result<Config, Error> {
    serde_json::from_str(data).map_err(Error::from)
}

#[cfg(feature = "verbose")]
macro_rules! verbose {
    ($($arg:tt)*) => { eprintln!("[VERBOSE] {}", format!($($arg)*)); }
}
#[cfg(not(feature = "verbose"))]
macro_rules! verbose {
-    ($($arg:tt)*) => {}; // Compiles to nothing
+    ($($arg:tt)*) => {}; // Compiles to nothing / 编译为空
}
}

-| C Preprocessor | Rust Feature Flags | +| C Preprocessor / C 预处理器 | Rust Feature Flags / Rust Feature 标志 | |—————|—————––| -| gcc -DDEBUG | cargo build --features verbose | +| gcc -DDEBUG | cargo build --features verbose | -| #ifdef DEBUG | #[cfg(feature = "verbose")] | +| #ifdef DEBUG | #[cfg(feature = "verbose")] | -| #define MAX 100 | const MAX: u32 = 100; | +| #define MAX 100 | const MAX: u32 = 100; | -| #ifdef __linux__ | #[cfg(target_os = "linux")] | +| #ifdef __linux__ | #[cfg(target_os = "linux")] |

  • Unit tests live next to the code with #[cfg(test)]. Integration tests live in
  • 单元测试使用 #[cfg(test)] 与代码共存。集成测试位于
  • tests/ and test your crate’s public API only:
  • tests/ 目录下,并且仅测试你的 crate 的公有 API:
#![allow(unused)]
fn main() {
- // tests/smoke_test.rs — no #[cfg(test)] needed
+ // tests/smoke_test.rs — no #[cfg(test)] needed / 不需要 #[cfg(test)]
use my_crate::parse_config;

#[test]
fn parse_valid_config() {
    let config = parse_config("test_data/valid.json").unwrap();
    assert_eq!(config.max_retries, 5);
}
}

-| Aspect | Unit Tests (#[cfg(test)]) | Integration Tests (tests/) | +| Aspect / 维度 | Unit Tests (#[cfg(test)]) / 单元测试 | Integration Tests (tests/) / 集成测试 | |––––|––––––––––––––|——————————| -| Location | Same file as code | Separate tests/ directory | +| Location / 位置 | Same file as code / 与代码在同一个文件 | Separate tests/ directory / 独立的 tests/ 目录 | -| Access | Private + public items | Public API only | +| Access / 访问权限 | Private + public items / 私有 + 公有项 | Public API only / 仅限公有 API | -| Run command | cargo test | cargo test --test smoke_test | +| Run command / 运行命令 | cargo test | cargo test --test smoke_test |

  • C firmware teams typically write tests in CUnit, CMocka, or custom frameworks with a
  • C 固件团队通常使用 CUnit、CMocka 或带有大量样板代码的自定义框架来编写测试。
  • lot of boilerplate. Rust’s built-in test harness is far more capable. This section
  • Rust 内置的测试工具功能更为强大。本节介绍生产代码中需要的模式。
#![allow(unused)]
fn main() {
- // Test that certain conditions cause panics (like C's assert failures)
+ // Test that certain conditions cause panics / 测试某些条件是否会导致 panic(类似于 C 的 assert 失败)
#[test]
#[should_panic(expected = "index out of bounds")]
fn test_bounds_check() {
    let v = vec![1, 2, 3];
-    let _ = v[10];  // Should panic
+    let _ = v[10];  // Should panic / 应该 panic
}

#[test]
#[should_panic(expected = "temperature exceeds safe limit")]
fn test_thermal_shutdown() {
    fn check_temperature(celsius: f64) {
        if celsius > 105.0 {
            panic!("temperature exceeds safe limit: {celsius}°C");
        }
    }
-    check_temperature(110.0);
+    check_temperature(110.0); // 出发 panic
}
}
#![allow(unused)]
fn main() {
- // Mark tests that require special conditions (like C's #ifdef HARDWARE_TEST)
+ // Mark tests that require special conditions / 标记需要特殊条件的测试(类似于 C 的 #ifdef HARDWARE_TEST)
#[test]
#[ignore = "requires GPU hardware"]
fn test_gpu_ecc_scrub() {
-    // This test only runs on machines with GPUs
+    // This test only runs on machines with GPUs / 此测试仅在带有 GPU 的机器上运行
    // Run with: cargo test -- --ignored
    // Run with: cargo test -- --include-ignored  (runs ALL tests)
}
}
#![allow(unused)]
fn main() {
- // Instead of many unwrap() calls that hide the actual failure:
+ // Instead of many unwrap() / 与其使用会隐藏实际故障的多次 unwrap() 调用:
#[test]
fn test_config_parsing() -> Result<(), Box<dyn std::error::Error>> {
    let json = r#"{"hostname": "node-01", "port": 8080}"#;
-    let config: ServerConfig = serde_json::from_str(json)?;  // ? instead of unwrap()
+    let config: ServerConfig = serde_json::from_str(json)?;  // ? / 使用 ? 代替 unwrap()
    assert_eq!(config.hostname, "node-01");
    assert_eq!(config.port, 8080);
-    Ok(())  // Test passes if we reach here without error
+    Ok(())  // Test passes / 如果到达这里且没有错误,测试评估为通过
}
}
  • C uses setUp()/tearDown() functions. Rust uses helper functions and Drop:
  • C 使用 setUp()/tearDown() 函数。Rust 使用辅助函数和 Drop trait:
#![allow(unused)]
fn main() {
struct TestFixture {
    temp_dir: std::path::PathBuf,
    config: Config,
}

impl TestFixture {
    fn new() -> Self {
        let temp_dir = std::env::temp_dir().join(format!("test_{}", std::process::id()));
        std::fs::create_dir_all(&temp_dir).unwrap();
        let config = Config {
            log_dir: temp_dir.clone(),
            max_retries: 3,
            ..Default::default()
        };
        Self { temp_dir, config }
    }
}

impl Drop for TestFixture {
    fn drop(&mut self) {
-        // Automatic cleanup — like C's tearDown() but can't be forgotten
+        // Automatic cleanup / 自动清理 —— 类似于 C 的 tearDown(),但不会被遗忘
        let _ = std::fs::remove_dir_all(&self.temp_dir);
    }
}

#[test]
fn test_with_fixture() {
    let fixture = TestFixture::new();
-    // Use fixture.config, fixture.temp_dir...
+    // Use fixture / 使用 fixture.config, fixture.temp_dir...
    assert!(fixture.temp_dir.exists());
-    // fixture is automatically dropped here → cleanup runs
+    // fixture is automatically dropped / fixture 在这里被自动 drop -> 运行清理
}
}
  • In C, mocking hardware requires preprocessor tricks or function pointer swapping.
  • 在 C 中,模拟硬件需要预处理器技巧或函数指针交换。
  • In Rust, traits make this natural:
  • 在 Rust 中,使用 trait 让这一切变得很自然:
#![allow(unused)]
fn main() {
- // Production trait for IPMI communication
+ // Production trait / 用于 IPMI 通信的生产级 trait
trait IpmiTransport {
    fn send_command(&self, cmd: u8, data: &[u8]) -> Result<Vec<u8>, String>;
}

- // Real implementation (used in production)
+ // Real implementation / 真实实现(用于生产)
struct RealIpmi { /* BMC connection details / BMC 连接详情 */ }
impl IpmiTransport for RealIpmi {
    fn send_command(&self, cmd: u8, data: &[u8]) -> Result<Vec<u8>, String> {
-        // Actually talks to BMC hardware
+        // Actually talks to BMC hardware / 实际与 BMC 硬件通信
        todo!("Real IPMI call")
    }
}

- // Mock implementation (used in tests)
+ // Mock implementation / 模拟实现(用于测试)
struct MockIpmi {
    responses: std::collections::HashMap<u8, Vec<u8>>,
}
impl IpmiTransport for MockIpmi {
    fn send_command(&self, cmd: u8, _data: &[u8]) -> Result<Vec<u8>, String> {
        self.responses.get(&cmd)
            .cloned()
            .ok_or_else(|| format!("No mock response for cmd 0x{cmd:02x}"))
    }
}

- // Generic function that works with both real and mock
+ // Generic function / 同时适用于真实和模拟的泛型函数
fn read_sensor_temperature(transport: &dyn IpmiTransport) -> Result<f64, String> {
    let response = transport.send_command(0x2D, &[])?;
    if response.len() < 2 {
        return Err("Response too short".into());
    }
    Ok(response[0] as f64 + (response[1] as f64 / 256.0))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_temperature_reading() {
        let mut mock = MockIpmi { responses: std::collections::HashMap::new() };
-        mock.responses.insert(0x2D, vec![72, 128]); // 72.5°C
+        mock.responses.insert(0x2D, vec![72, 128]); // 模拟 72.5°C

        let temp = read_sensor_temperature(&mock).unwrap();
        assert!((temp - 72.5).abs() < 0.01);
    }

    #[test]
    fn test_short_response() {
        let mock = MockIpmi { responses: std::collections::HashMap::new() };
-        // No response configured → error
+        // No response configured / 未配置响应 -> 报错
        assert!(read_sensor_temperature(&mock).is_err());
    }
}
}
  • Instead of testing specific values, test properties that must always hold:
  • 与其测试特定数值,不如测试必须始终成立的属性(properties)
#![allow(unused)]
fn main() {
- // Cargo.toml: [dev-dependencies] proptest = "1"
+ // Cargo.toml: [dev-dependencies] proptest = "1" / 开发依赖
use proptest::prelude::*;

fn parse_sensor_id(s: &str) -> Option<u32> {
    s.strip_prefix("sensor_")?.parse().ok()
}

fn format_sensor_id(id: u32) -> String {
    format!("sensor_{id}")
}

proptest! {
    #[test]
    fn roundtrip_sensor_id(id in 0u32..10000) {
-        // Property: format then parse should give back the original
+        // Property: format then parse should give back the original / 属性:格式化后再解析应返回原始值
        let formatted = format_sensor_id(id);
        let parsed = parse_sensor_id(&formatted);
        prop_assert_eq!(parsed, Some(id));
    }

    #[test]
    fn parse_rejects_garbage(s in "[^s].*") {
-        // Property: strings not starting with 's' should never parse
+        // Property: strings not starting with 's' should never parse / 属性:不以 's' 开头的字符串永远不应被解析成功
        let result = parse_sensor_id(&s);
        prop_assert!(result.is_none());
    }
}
}

-| C Testing | Rust Equivalent | +| C Testing / C 测试 | Rust Equivalent / Rust 等价物 | |———–|––––––––| -| CUnit, CMocka, custom framework | Built-in #[test] + cargo test | +| CUnit, CMocka, custom framework / 自定义框架 | Built-in #[test] + cargo test / 内置支持 | -| setUp() / tearDown() | Builder function + Drop trait | +| setUp() / tearDown() | Builder function + Drop trait / 构建器函数 + Drop | -| #ifdef TEST mock functions | Trait-based dependency injection | +| #ifdef TEST mock functions | Trait-based dependency injection / 基于 Trait 的依赖注入 | -| assert(x == y) | assert_eq!(x, y) with auto diff output | +| assert(x == y) | assert_eq!(x, y) (带自动 diff 输出) | -| Separate test executable | Same binary, conditional compilation with #[cfg(test)] | +| Separate test executable / 独立的测试可执行文件 | Same binary, conditional compilation / 同一二进制文件,条件编译 | -| valgrind --leak-check=full ./test | cargo test (memory safe by default) + cargo miri test | +| valgrind --leak-check=full ./test | cargo test (默认内存安全) + cargo miri test | -| Code coverage: gcov / lcov | cargo tarpaulin or cargo llvm-cov | +| Code coverage / 代码覆盖率: gcov / lcov | cargo tarpaulin or cargo llvm-cov | -| Test discovery: manual registration | Automatic — any #[test] fn is discovered | +| Test discovery / 测试发现: 手动注册 | Automatic / 自动 —— 任何 #[test] 函数都会被发现 |

Testing Patterns / 测试模式

Testing Patterns for C++ Programmers / C++ 程序员的测试模式

What you’ll learn / 你将学到: Rust’s built-in test framework — #[test], #[should_panic], Result-returning tests, builder patterns for test data, trait-based mocking, property testing with proptest, snapshot testing with insta, and integration test organization. Zero-config testing that replaces Google Test + CMake.

Rust 内置的测试框架 —— #[test]#[should_panic]、返回 Result 的测试、测试数据的构建器模式、基于 trait 的模拟(mocking)、使用 proptest 进行属性测试、使用 insta 进行快照测试,以及集成测试的组织。这种零配置测试将取代 Google Test + CMake。

  • C++ testing typically relies on external frameworks (Google Test, Catch2, Boost.Test)
  • C++ 测试通常依赖于外部框架(Google Test、Catch2、Boost.Test)
  • with complex build integration. Rust’s test framework is **built into the language
  • 并伴随着复杂的构建集成。Rust 的测试框架是内置于语言和工具链中的 —— 无需依赖,无需 CMake 集成,也无需配置测试运行器(test runner)。
#![allow(unused)]
fn main() {
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn basic_pass() {
        assert_eq!(2 + 2, 4);
    }

-    // Expect a panic — equivalent to GTest's EXPECT_DEATH
+    // Expect a panic — equivalent to GTest's EXPECT_DEATH / 预期发生 panic —— 相当于 GTest 的 EXPECT_DEATH
    #[test]
    #[should_panic]
    fn out_of_bounds_panics() {
        let v = vec![1, 2, 3];
-        let _ = v[10]; // Panics — test passes
+        let _ = v[10]; // Panics — test passes / 发生 panic —— 测试通过
    }

-    // Expect a panic with a specific message substring
+    // Expect a panic with a specific message substring / 预期带有特定消息子串的 panic
    #[test]
    #[should_panic(expected = "index out of bounds")]
    fn specific_panic_message() {
        let v = vec![1, 2, 3];
        let _ = v[10];
    }

-    // Tests that return Result<(), E> — use ? instead of unwrap()
+    // Tests that return Result<(), E> — use ? instead of unwrap() / 返回 Result<(), E> 的测试 —— 使用 ? 代替 unwrap()
    #[test]
    fn test_with_result() -> Result<(), String> {
        let value: u32 = "42".parse().map_err(|e| format!("{e}"))?;
        assert_eq!(value, 42);
        Ok(())
    }

-    // Ignore slow tests by default — run with `cargo test -- --ignored`
+    // Ignore slow tests by default — run with `cargo test -- --ignored` / 默认忽略慢速测试 —— 使用 `cargo test -- --ignored` 运行
    #[test]
    #[ignore]
    fn slow_integration_test() {
        std::thread::sleep(std::time::Duration::from_secs(10));
    }
}
}
- cargo test                          # Run all non-ignored tests
+ cargo test                          # Run all non-ignored tests / 运行所有未被忽略的测试
- cargo test -- --ignored             # Run only ignored tests
+ cargo test -- --ignored             # Run only ignored tests / 仅运行被忽略的测试
- cargo test -- --include-ignored     # Run ALL tests including ignored
+ cargo test -- --include-ignored     # Run ALL tests including ignored / 运行包括忽略测试在内的所有测试
- cargo test test_name                # Run tests matching a name pattern
+ cargo test test_name                # Run tests matching a name pattern / 运行匹配名称模式的测试
- cargo test -- --nocapture           # Show println! output during tests
+ cargo test -- --nocapture           # Show println! output during tests / 测试期间显示 println! 输出
- cargo test -- --test-threads=1      # Run tests serially (for shared state)
+ cargo test -- --test-threads=1      # Run tests serially (for shared state) / 串行运行测试(用于共享状态)
  • In C++ you’d use Google Test fixtures (class MyTest : public ::testing::Test).
  • 在 C++ 中,你会使用 Google Test 固件(Google Test fixtures,class MyTest : public ::testing::Test)。
  • In Rust, use builder functions or the Default trait:
  • 在 Rust 中,使用构建器函数或 Default trait:
#![allow(unused)]
fn main() {
#[cfg(test)]
mod tests {
    use super::*;

-    // Builder function — creates test data with sensible defaults
+    // Builder function — creates test data with sensible defaults / 构建器函数 —— 使用合理的默认值创建测试数据
    fn make_gpu_event(severity: Severity, fault_code: u32) -> DiagEvent {
        DiagEvent {
            source: "accel_diag".to_string(),
            severity,
            message: format!("Test event FC:{fault_code}"),
            fault_code,
        }
    }

-    // Reusable test fixture — a set of pre-built events
+    // Reusable test fixture — a set of pre-built events / 可重用的测试固件 —— 一组预构建的事件
    fn sample_events() -> Vec<DiagEvent> {
        vec![
            make_gpu_event(Severity::Critical, 67956),
            make_gpu_event(Severity::Warning, 32709),
            make_gpu_event(Severity::Info, 10001),
        ]
    }

    #[test]
    fn filter_critical_events() {
        let events = sample_events();
        let critical: Vec<_> = events.iter()
            .filter(|e| e.severity == Severity::Critical)
            .collect();
        assert_eq!(critical.len(), 1);
        assert_eq!(critical[0].fault_code, 67956);
    }
}
}
  • In C++, mocking requires frameworks like Google Mock or manual virtual overrides.
  • 在 C++ 中,模拟(mocking)需要像 Google Mock 这样的框架或手动进行虚函数覆盖。
  • In Rust, define a trait for the dependency and swap implementations in tests:
  • 在 Rust 中,为依赖项定义一个 trait 并在测试中更换实现:
#![allow(unused)]
fn main() {
- // Production trait
+ // Production trait / 生产环境使用的 trait
trait SensorReader {
    fn read_temperature(&self, sensor_id: u32) -> Result<f64, String>;
}

- // Production implementation
+ // Production implementation / 生产环境实现
struct HwSensorReader;
impl SensorReader for HwSensorReader {
    fn read_temperature(&self, sensor_id: u32) -> Result<f64, String> {
-        // Real hardware call...
+        // Real hardware call... / 真实的硬件调用...
        Ok(72.5)
    }
}

- // Test mock — returns predictable values
+ // Test mock — returns predictable values / 测试模拟 —— 返回可预测的值
#[cfg(test)]
struct MockSensorReader {
    temperatures: std::collections::HashMap<u32, f64>,
}

#[cfg(test)]
impl SensorReader for MockSensorReader {
    fn read_temperature(&self, sensor_id: u32) -> Result<f64, String> {
        self.temperatures.get(&sensor_id)
            .copied()
            .ok_or_else(|| format!("Unknown sensor {sensor_id}"))
    }
}

- // Function under test — generic over the reader
+ // Function under test — generic over the reader / 待测函数 —— 对 reader 进行泛型处理
fn check_overtemp(reader: &impl SensorReader, ids: &[u32], threshold: f64) -> Vec<u32> {
    ids.iter()
        .filter(|&&id| reader.read_temperature(id).unwrap_or(0.0) > threshold)
        .copied()
        .collect()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn detect_overtemp_sensors() {
        let mut mock = MockSensorReader { temperatures: Default::default() };
-        mock.temperatures.insert(0, 72.5);
+        mock.temperatures.insert(0, 72.5); // 存入模拟值
-        mock.temperatures.insert(1, 91.0);  // Over threshold
+        mock.temperatures.insert(1, 91.0);  // Over threshold / 超过阈值
        mock.temperatures.insert(2, 65.0);

        let hot = check_overtemp(&mock, &[0, 1, 2], 80.0);
        assert_eq!(hot, vec![1]);
    }
}
}
  • C++ tests often use platform-specific temp directories. Rust has tempfile:
  • C++ 测试通常使用特定于平台的临时目录。Rust 有 tempfile
#![allow(unused)]
fn main() {
// Cargo.toml: [dev-dependencies]
// tempfile = "3"

#[cfg(test)]
mod tests {
    use super::*;
    use tempfile::NamedTempFile;
    use std::io::Write;

    #[test]
    fn parse_config_from_file() -> Result<(), Box<dyn std::error::Error>> {
-        // Create a temp file that's auto-deleted when dropped
+        // Create a temp file that's auto-deleted when dropped / 创建一个在释放(dropped)时自动删除的临时文件
        let mut file = NamedTempFile::new()?;
        writeln!(file, r#"{{"sku": "ServerNode", "level": "Quick"}}"#)?;

        let config = load_config(file.path().to_str().unwrap())?;
        assert_eq!(config.sku, "ServerNode");
        Ok(())
-        // file is deleted here — no cleanup code needed
+        // file is deleted here — no cleanup code needed / 文件在这里被删除 —— 无需清理代码
    }
}
}
  • Instead of writing specific test cases, describe properties that should hold
  • 与其编写特定的测试用例,不如描述对于所有输入都应成立的属性(properties)
  • for all inputs. proptest generates random inputs and finds minimal failing cases:
  • proptest 会生成随机输入并找到最小的失败用例:
#![allow(unused)]
fn main() {
// Cargo.toml: [dev-dependencies]
// proptest = "1"

#[cfg(test)]
mod tests {
    use proptest::prelude::*;

    fn parse_and_format(n: u32) -> String {
        format!("{n}")
    }

    proptest! {
        #[test]
-        fn roundtrip_u32(n: u32) {
+        fn roundtrip_u32(n: u32) { // 测试往返转换
            let formatted = parse_and_format(n);
            let parsed: u32 = formatted.parse().unwrap();
            prop_assert_eq!(n, parsed);
        }

        #[test]
-        fn string_contains_no_null(s in "[a-zA-Z0-9 ]{0,100}") {
+        fn string_contains_no_null(s in "[a-zA-Z0-9 ]{0,100}") { // 字符串不包含 null
            prop_assert!(!s.contains('\0'));
        }
    }
}
}
  • For tests that produce complex output (JSON, formatted strings), insta auto-generates
  • 对于产生复杂输出(JSON、格式化字符串)的测试,insta 会自动生成并管理参考快照:
  • and manages reference snapshots:
#![allow(unused)]
fn main() {
// Cargo.toml: [dev-dependencies]
// insta = { version = "1", features = ["json"] }

#[cfg(test)]
mod tests {
    use insta::assert_json_snapshot;

    #[test]
    fn der_entry_format() {
        let entry = DerEntry {
            fault_code: 67956,
            component: "GPU".to_string(),
            message: "ECC error detected".to_string(),
        };
-        // First run: creates a snapshot file in tests/snapshots/
+        // First run: creates a snapshot file in tests/snapshots/ / 第一次运行:在 tests/snapshots/ 中创建一个快照文件
-        // Subsequent runs: compares against the saved snapshot
+        // Subsequent runs: compares against the saved snapshot / 后续运行:与保存的快照进行比较
        assert_json_snapshot!(entry);
    }
}
}
- cargo insta test              # Run tests and review new/changed snapshots
+ cargo insta test              # Run tests and review snapshots / 运行测试并查看新快照或变更的快照
- cargo insta review            # Interactive review of snapshot changes
+ cargo insta review            # Interactive review of snapshot changes / 以交互方式查看快照变更

-| C++ (Google Test) | Rust | Notes | +| C++ (Google Test) | Rust | Notes / 说明 | |–––––––––––|———|–––––| -| TEST(Suite, Name) { } | #[test] fn name() { } | No suite/class hierarchy needed | +| TEST(Suite, Name) { } | #[test] fn name() { } | No suite/class hierarchy needed / 无需测试套件或类层次结构 | -| ASSERT_EQ(a, b) | assert_eq!(a, b) | Built-in macro, no framework needed | +| ASSERT_EQ(a, b) | assert_eq!(a, b) | Built-in macro, no framework needed / 内置宏,无需框架 | -| ASSERT_NEAR(a, b, eps) | assert!((a - b).abs() < eps) | Or use approx crate | +| ASSERT_NEAR(a, b, eps) | assert!((a - b).abs() < eps) | Or use approx crate / 或者使用 approx crate | -| EXPECT_THROW(expr, type) | #[should_panic(expected = "...")] | Or catch_unwind for fine control | +| EXPECT_THROW(expr, type) | #[should_panic(expected = "...")] | Or catch_unwind / 或使用 catch_unwind 进行精细控制 | -| EXPECT_DEATH(expr, "msg") | #[should_panic(expected = "msg")] | | -| EXPECT_DEATH(expr, "msg") | #[should_panic(expected = "msg")] | | -| class Fixture : public ::testing::Test | Builder functions + Default | No inheritance needed | +| class Fixture : public ::testing::Test | Builder functions + Default | No inheritance needed / 无需继承 | -| Google Mock MOCK_METHOD | Trait + test impl | More explicit, no macro magic | +| Google Mock MOCK_METHOD | Trait + test impl | More explicit, no macro magic / 更显式,无宏魔法 | -| INSTANTIATE_TEST_SUITE_P (parameterized) | proptest! or macro-generated tests | | -| INSTANTIATE_TEST_SUITE_P (parameterized) | proptest! or macro-generated tests | | -| SetUp() / TearDown() | RAII via Drop — cleanup is automatic | Variables dropped at end of test | +| SetUp() / TearDown() | RAII via Drop — cleanup is automatic | Variables dropped at end of test / 变量在测试结束时被释放 | -| Separate test binary + CMake | cargo test — zero config | | -| Separate test binary + CMake | cargo test — zero config / 零配置 | | -| ctest --output-on-failure | cargo test -- --nocapture | | -| ctest --output-on-failure | cargo test -- --nocapture | |


  • Unit tests live inside #[cfg(test)] modules alongside your code. Integration tests live in a separate tests/ directory at the crate root and test your library’s public API as an external consumer would:
  • 单元测试位于代码旁边的 #[cfg(test)] 模块中。集成测试位于 crate 根目录下的独立 tests/ 目录中,并像外部消费者一样测试你的库的公有 API:
- my_crate/
+ my_crate/ # 你的 crate
├── src/
- │   └── lib.rs          # Your library code
+ │   └── lib.rs          # Your library code / 你的库代码
├── tests/
- │   ├── smoke.rs        # Each .rs file is a separate test binary
+ │   ├── smoke.rs        # Each .rs file is a separate test binary / 每个 .rs 文件都是一个独立的测试二进制文件
- │   ├── regression.rs
- │   └── common/
- │       └── mod.rs      # Shared test helpers (NOT a test itself)
+ │   ├── regression.rs   # 回归测试
+ │   └── common/ # 测试辅助工具
+ │       └── mod.rs      # Shared test helpers (NOT a test itself) / 共享测试辅助工具(其本身不是测试)
└── Cargo.toml
#![allow(unused)]
fn main() {
- // tests/smoke.rs — tests your crate as an external user would
+ // tests/smoke.rs — tests your crate as an external user would / 像外部用户一样测试你的 crate
- use my_crate::DiagEngine;  // Only public API is accessible
+ use my_crate::DiagEngine;  // Only public API is accessible / 仅可访问公有 API

#[test]
fn engine_starts_successfully() {
    let engine = DiagEngine::new("test_config.json");
    assert!(engine.is_ok());
}

#[test]
fn engine_rejects_invalid_config() {
    let engine = DiagEngine::new("nonexistent.json");
    assert!(engine.is_err());
}
}
#![allow(unused)]
fn main() {
- // tests/common/mod.rs — shared helpers, NOT compiled as a test binary
+ // tests/common/mod.rs — shared helpers / 共享辅助工具,不会被编译为测试二进制文件
pub fn setup_test_environment() -> tempfile::TempDir {
    let dir = tempfile::tempdir().unwrap();
    std::fs::write(dir.path().join("config.json"), r#"{"log_level": "debug"}"#).unwrap();
    dir
}
}
#![allow(unused)]
fn main() {
- // tests/regression.rs — can use shared helpers
+ // tests/regression.rs — can use shared helpers / 可以使用共享辅助工具
mod common;

#[test]
fn regression_issue_42() {
    let env = common::setup_test_environment();
    let engine = my_crate::DiagEngine::new(
        env.path().join("config.json").to_str().unwrap()
    );
    assert!(engine.is_ok());
}
}
  • Running integration tests:
  • Running integration tests / 运行集成测试:
- cargo test                          # Runs unit AND integration tests
+ cargo test                          # Runs unit AND integration tests / 运行单元测试和集成测试
- cargo test --test smoke             # Run only tests/smoke.rs
+ cargo test --test smoke             # Run only tests/smoke.rs / 仅运行 tests/smoke.rs
- cargo test --test regression        # Run only tests/regression.rs
+ cargo test --test regression        # Run only tests/regression.rs / 仅运行 tests/regression.rs
- cargo test --lib                    # Run ONLY unit tests (skip integration)
+ cargo test --lib                    # Run ONLY unit tests (skip integration) / 仅运行单元测试(跳过集成测试)
  • Key difference from unit tests: Integration tests cannot access private functions or pub(crate) items. This forces you to verify that your public API is sufficient — a valuable design signal. In C++ terms, it’s like testing against only the public header with no friend access.

  • 与单元测试的关键区别:集成测试无法访问私有函数或 pub(crate) 项。这会强制你验证公有 API 是否足够 —— 这是一个宝贵的辅助设计的信号。用 C++ 的话来说,这就像是在没有任何 friend 访问权限的情况下,仅针对公有头文件进行测试。

9. Error Handling / 9. 错误处理

Connecting enums to Option and Result / 将枚举与 Option 和 Result 联系起来

What you’ll learn / 你将学到: How Rust replaces null pointers with Option<T> and exceptions with Result<T, E>, and how the ? operator makes error propagation concise. This is Rust’s most distinctive pattern — errors are values, not hidden control flow.

Rust 如何用 Option<T> 取代空指针,用 Result<T, E> 取代异常,以及 ? 运算符如何使错误传播变得简洁。这是 Rust 最鲜明的模式 —— 错误是值,而不是隐藏的控制流。

  • Remember the enum type we learned earlier? Rust’s Option and Result are simply enums defined in the standard library:
  • 还记得我们之前学过的 enum 类型吗?Rust 的 OptionResult 只是标准库中定义的简单枚举:
#![allow(unused)]
fn main() {
-// This is literally how Option is defined in std:
+// This is literally how Option is defined in std / 这确实是 Option 在 std 中的定义方式:
enum Option<T> {
-    Some(T),  // Contains a value
+    Some(T),  // Contains a value / 包含一个值
-    None,     // No value
+    None,     // No value / 无值
}

-// And Result:
+// And Result / 以及 Result:
enum Result<T, E> {
-    Ok(T),    // Success with value
+    Ok(T),    // Success with value / 成功并包含值
-    Err(E),   // Error with details
+    Err(E),   // Error with details / 错误及其详情
}
}
    • This means everything you learned about pattern matching with match works directly with Option and Result
    • 这意味着你学到的所有关于 match 模式匹配的知识都可以直接应用于 OptionResult
    • There is no null pointer in Rust – Option<T> is the replacement, and the compiler forces you to handle the None case
    • Rust 中没有空指针 —— Option<T> 是其替代方案,且编译器会强制你处理 None 的情况

-| C++ Pattern | Rust Equivalent | Advantage | +| C++ Pattern / C++ 模式 | Rust Equivalent / Rust 等价物 | Advantage / 优势 | |––––––––|––––––––––|–––––––| -| throw std::runtime_error(msg) | Err(MyError::Runtime(msg)) | Error in return type — can’t forget to handle | +| throw std::runtime_error(msg) | Err(MyError::Runtime(msg)) | Error in return type / 错误包含在返回类型中 —— 不会忘记处理 | -| try { } catch (...) { } | match result { Ok(v) => ..., Err(e) => ... } | No hidden control flow | +| try { } catch (...) { } | match result { ... } | No hidden control flow / 无隐藏控制流 | -| std::optional<T> | Option<T> | Exhaustive match required — can’t forget None | +| std::optional<T> | Option<T> | Exhaustive match required / 必须穷尽匹配 —— 不会遗忘 None | -| noexcept annotation | Default — all Rust functions are “noexcept” | Exceptions don’t exist | +| noexcept annotation / 注解 | Default / 默认 —— 所有 Rust 函数都是 “noexcept” | Exceptions don’t exist / 并不存在异常 | -| errno / return codes | Result<T, E> | Type-safe, can’t ignore | +| errno / return codes / 返回码 | Result<T, E> | Type-safe, can’t ignore / 类型安全,无法被忽略 |

    • The Rust Option type is an enum with only two variants: Some<T> and None
    • Rust Option 类型是一个只有两个变体的 enumSome<T>None
  • - The idea is that this represents a ```nullable``` type, i.e., it either contains a valid value of that type (```Some<T>```), or has no valid value (```None```)
    
  • - 其核心理念是代表一个“可空(nullable)”类型,即它要么包含一个该类型的有效值(```Some<T>```),要么没有有效值(```None```)
    
  • - The ```Option``` type is used in APIs result of an operation either succeeds and returns a valid value or it fails (but the specific error is irrelevant). For example, consider parsing a string for an integer value
    
  • - ```Option``` 类型常用于操作结果要么成功并返回有效值,要么失败(但具体错误细节无关紧要)的 API 中。例如,考虑在字符串中搜索某个字符的索引:
    
fn main() {
-    // Returns Option<usize>
+    // Returns Option<usize> / 返回 Option<usize>
    let a = "1234".find("1");
    match a {
        Some(a) => println!("Found 1 at index {a}"),
        None => println!("Couldn't find 1")
    }
}
    • Rust Option can be processed in various ways
    • Rust Option 可以通过多种方式处理
  • - ```unwrap()``` panics if the ```Option<T>``` is ```None``` and returns ```T``` otherwise and it is the least preferred approach 
    
  • - ```unwrap()``` 如果 ```Option<T>``` 是 ```None``` 则会发生 panic,否则返回 ```T```。这是最不推荐的方法。
    
  • - ```or()``` can be used to return an alternative value 
    
  • - ```or()``` 可用于返回一个替代值
    
  • ```if let``` lets us test for ```Some<T>```
    
  • - ```if let``` 让我们能够便捷地测试 ```Some<T>```
    
fn main() {
-  // This return an Option<usize>
+  // This return an Option<usize> / 这返回一个 Option<usize>
  let a = "1234".find("1");
  println!("{a:?} {}", a.unwrap());
  let a = "1234".find("5").or(Some(42));
  println!("{a:?}");
  if let Some(a) = "1234".find("1") {
      println!("{a}");
  } else {
-    println!("Not found in string");
+    println!("Not found in string / 字符串中未找到");
  }
-  // This will panic
+  // This will panic / 这会发生 panic
  // "1234".find("5").unwrap();
}
    • Result is an enum type similar to Option with two variants: Ok<T> or Err<E>
    • Result 是一种类似于 Optionenum 类型,具有两个变体:Ok<T>Err<E>
  • - ```Result``` is used extensively in Rust APIs that can fail. The idea is that on success, functions will return a ```Ok<T>```, or they will return a specific error ```Err<T>```
    
  • - ```Result``` 在可能失败的 Rust API 中被广泛使用。其核心理念是:成功时函数返回 ```Ok<T>```,失败时返回具体的错误 ```Err<E>```。
    
  use std::num::ParseIntError;
  fn main() {
  let a : Result<i32, ParseIntError>  = "1234z".parse();
  match a {
      Ok(n) => println!("Parsed {n}"),
      Err(e) => println!("Parsing failed {e:?}"),
  }
  let a : Result<i32, ParseIntError>  = "1234z".parse().or(Ok(-1));
  println!("{a:?}");
  if let Ok(a) = "1234".parse::<i32>() {
    println!("Let OK {a}");  
  }
-  // This will panic
+  // This will panic / 这会发生 panic
  //"1234z".parse().unwrap();
}
  • Option and Result are deeply related — Option<T> is essentially Result<T, ()> (a result where the error carries no information):
  • OptionResult 深度契合 —— Option<T> 本质上是 Result<T, ()>(一种错误信息不携带任何内容的 Result):

-| Option<T> | Result<T, E> | Meaning | +| Option<T> | Result<T, E> | Meaning / 含义 | |———––|—————|———| -| Some(value) | Ok(value) | Success — value is present | +| Some(value) | Ok(value) | Success / 成功 —— 值存在 | -| None | Err(error) | Failure — no value (Option) or error details (Result) | +| None | Err(error) | Failure / 失败 —— 无值 (Option) 或错误详情 (Result) |

  • Converting between them:
  • Converting between them / 两者之间的转换:
fn main() {
    let opt: Option<i32> = Some(42);
-    let res: Result<i32, &str> = opt.ok_or("value was None");  // Option → Result
+    let res: Result<i32, &str> = opt.ok_or("value was None");  // Option → Result / 将 Option 转换为 Result
    
    let res: Result<i32, &str> = Ok(42);
-    let opt: Option<i32> = res.ok();  // Result → Option (discards error)
+    let opt: Option<i32> = res.ok();  // Result → Option / 将 Result 转换为 Option(丢弃错误)
    
-    // They share many of the same methods:
+    // They share many of the same methods / 它们共享许多相同的方法:
    // .map(), .and_then(), .unwrap_or(), .unwrap_or_else(), .is_some()/is_ok()
}
  • Rule of thumb: Use Option when absence is normal (e.g., looking up a key). Use Result when failure needs explanation (e.g., file I/O, parsing).

  • 经验法则:当“缺失”是预期内的正常情况时(例如:查找键值对),使用 Option。当“失败”需要解释时(例如:文件 I/O、转换),使用 Result

  • 🟢 Starter
  • 🟢 Starter / 入门级
    • Implement a log() function that accepts an Option<&str> parameter. If the parameter is None, it should print a default string
    • 实现一个接受 Option<&str> 参数的 log() 函数。如果参数为 None,则应打印默认字符串
    • The function should return a Result with () for both success and error (in this case we’ll never have an error)
    • 函数应返回一个 Result,成功和错误对应的类型都是 ()(本例中我们永远不会出错)
  • Solution (click to expand)
  • Solution (click to expand) / 解决方案(点击展开)
fn log(message: Option<&str>) -> Result<(), ()> {
    match message {
        Some(msg) => println!("LOG: {msg}"),
        None => println!("LOG: (no message provided)"),
    }
    Ok(())
}

fn main() {
    let _ = log(Some("System initialized"));
    let _ = log(None);
    
-    // Alternative using unwrap_or:
+    // Alternative using unwrap_or / 使用 unwrap_or 的替代方案:
    let msg: Option<&str> = None;
    println!("LOG: {}", msg.unwrap_or("(default message)"));
}
- // Output:
+ // Output / 输出:
// LOG: System initialized
// LOG: (no message provided)
// LOG: (default message)

    • Rust errors can be irrecoverable (fatal) or recoverable. Fatal errors result in a ``panic```
    • Rust 错误可以是不可恢复的(致命的)或可恢复的。致命错误会导致 ``panic```
  • - In general, situation that result in ```panics``` should be avoided. ```panics``` are caused by bugs in the program, including exceeding index bounds, calling ```unwrap()``` on an ```Option<None>```, etc.
    
  • - 通常应避免会导致 ```panic``` 的情况。```panic``` 是由程序中的 bug 引起的,包括超出索引范围、对 ```Option<None>``` 调用 ```unwrap()``` 等。
    
  • - It is OK to have explicit ```panics``` for conditions that should be impossible. The ```panic!``` or ```assert!``` macros can be used for sanity checks
    
  • - 对于本该不可能发生的条件,可以使用显式的 ```panic```。```panic!``` 或 ```assert!``` 宏可用于完整性检查(sanity checks)。
    
fn main() {
   let x : Option<u32> = None;
-   // println!("{x}", x.unwrap()); // Will panic
+   // println!("{x}", x.unwrap()); // Will panic / 会发生 panic
-   println!("{}", x.unwrap_or(0));  // OK -- prints 0
+   println!("{}", x.unwrap_or(0));  // OK -- prints 0 / OK —— 打印 0
   let x = 41;
-   //assert!(x == 42); // Will panic
+   //assert!(x == 42); // Will panic / 会发生 panic
-   //panic!("Something went wrong"); // Unconditional panic
+   //panic!("Something went wrong"); // Unconditional panic / 无条件 panic
   let _a = vec![0, 1];
-   // println!("{}", a[2]); // Out of bounds panic; use a.get(2) which will return Option<T>
+   // println!("{}", a[2]); // Out of bounds panic / 越界 panic;使用 a.get(2) 会返回 Option<T>
}
// C++ error handling - exceptions create hidden control flow
+// C++ 错误处理 —— 异常创建了隐藏的控制流
#include <fstream>
#include <stdexcept>

std::string read_config(const std::string& path) {
    std::ifstream file(path);
    if (!file.is_open()) {
        throw std::runtime_error("Cannot open: " + path);
    }
    std::string content;
-    // What if getline throws? Is file properly closed?
+    // What if getline throws? Is file properly closed? / 如果 getline 抛出异常怎么办?文件是否被正确关闭?
-    // With RAII yes, but what about other resources?
+    // With RAII yes, but what about other resources? / 有了 RAII 是的,但其他资源呢?
    std::getline(file, content);
-    return content;  // What if caller doesn't try/catch?
+    return content;  // What if caller doesn't try/catch? / 如果调用者没有进行 try/catch 怎么办?
}

int main() {
-    // ERROR: Forgot to wrap in try/catch!
+    // ERROR: Forgot to wrap in try/catch! / 错误:忘记在 try/catch 中包装!
    auto config = read_config("nonexistent.txt");
-    // Exception propagates silently, program crashes
+    // Exception propagates silently, program crashes / 异常静默传播,程序崩溃
-    // Nothing in the function signature warned us
+    // Nothing in the function signature warned us / 函数签名中没有任何告警
    return 0;
}
graph TD
-    subgraph "C++ Error Handling Issues"
+    subgraph "C++ Error Handling Issues / C++ 错误处理问题"
-        CF["Function Call"]
+        CF["Function Call / 函数调用"]
-        CR["throw exception<br/>or return code"]
+        CR["throw exception / 抛出异常<br/>or return code / 或返回码"]
-        CIGNORE["[ERROR] Exception not caught<br/>or return code ignored"]
+        CIGNORE["[ERROR] Exception not caught / 异常未捕捉<br/>or return code ignored / 或返回码被忽略"]
-        CCHECK["try/catch or check"]
+        CCHECK["try/catch or check / 捕捉或检查"]
-        CERROR["Hidden control flow<br/>throws not in signature"]
+        CERROR["Hidden control flow / 隐藏控制流<br/>throws not in signature / 签名无异常声明"]
-        CERRNO["No compile-time<br/>enforcement"]
+        CERRNO["No compile-time / 无编译时<br/>enforcement / 强制约束"]
         
         CF --> CR
         CR --> CIGNORE
         CR --> CCHECK
         CCHECK --> CERROR
         CERROR --> CERRNO
         
-        CPROBLEMS["[ERROR] Exceptions invisible in types<br/>[ERROR] Hidden control flow<br/>[ERROR] Easy to forget try/catch<br/>[ERROR] Exception safety is hard<br/>[ERROR] noexcept is opt-in"]
+        CPROBLEMS["[ERROR] Exceptions invisible in types / 类型中不可见<br/>[ERROR] Hidden control flow / 隐藏控制流<br/>[ERROR] Easy to forget try/catch / 易遗忘<br/>[ERROR] Exception safety is hard / 异常安全困难<br/>[ERROR] noexcept is opt-in / noexcept 需要主动选择"]
     end
     
-    subgraph "Rust Result<T, E> System"
+    subgraph "Rust Result<T, E> System / Result 系统"
-        RF["Function Call"]
+        RF["Function Call / 函数调用"]
-        RR["Result<T, E><br/>Ok(value) | Err(error)"]
+        RR["Result<T, E><br/>Ok(value) | Err(error)"]
-        RMUST["[OK] Must handle<br/>Compile error if ignored"]
+        RMUST["[OK] Must handle / 必须处理<br/>Compile error if ignored / 忽略则报错"]
-        RMATCH["Pattern matching<br/>match, if let, ?"]
+        RMATCH["Pattern matching / 模式匹配<br/>match, if let, ?"]
-        RDETAIL["Detailed error info<br/>Custom error types"]
+        RDETAIL["Detailed error info / 详尽错误信息<br/>Custom error types / 自定义错误类型"]
-        RSAFE["Type-safe<br/>No global state"]
+        RSAFE["Type-safe / 类型安全<br/>No global state / 无全局状态"]
         
         RF --> RR
         RR --> RMUST
         RMUST --> RMATCH
         RMATCH --> RDETAIL
         RDETAIL --> RSAFE
         
-        RBENEFITS["[OK] Forced error handling<br/>[OK] Type-safe errors<br/>[OK] Detailed error info<br/>[OK] Composable with ?<br/>[OK] Zero runtime cost"]
+        RBENEFITS["[OK] Forced error handling / 强制错误处理<br/>[OK] Type-safe errors / 类型安全错误<br/>[OK] Detailed error info / 详尽信息<br/>[OK] Composable with ? / 可用 ? 组合<br/>[OK] Zero runtime cost / 零运行时成本"]
     end
     
     style CPROBLEMS fill:#ff6b6b,color:#000
     style RBENEFITS fill:#91e5a3,color:#000
     style CIGNORE fill:#ff6b6b,color:#000
     style RMUST fill:#91e5a3,color:#000
// Rust error handling - comprehensive and forced
+// Rust 错误处理 —— 全面且强制
use std::fs::File;
use std::io::Read;

fn read_file_content(filename: &str) -> Result<String, std::io::Error> {
-    let mut file = File::open(filename)?;  // ? automatically propagates errors
+    let mut file = File::open(filename)?;  // ? automatically propagates errors / ? 自动传播错误
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
-    Ok(contents)  // Success case
+    Ok(contents)  // Success case / 成功情况
}

fn main() {
    match read_file_content("example.txt") {
        Ok(content) => println!("File content: {}", content),
        Err(error) => println!("Failed to read file: {}", error),
-        // Compiler forces us to handle both cases!
+        // Compiler forces us to handle both cases! / 编译器强制我们处理这两种情况!
    }
}
graph TD
-    subgraph "Result<T, E> Flow"
+    subgraph "Result<T, E> Flow / 流转"
-        START["Function starts"]
+        START["Function starts / 函数开始"]
-        OP1["File::open()"]
+        OP1["File::open() / 打开文件"]
-        CHECK1{{"Result check"}}
+        CHECK1{{"Result check / 结果检查"}}
-        OP2["file.read_to_string()"]
+        OP2["file.read_to_string() / 读取文件"]
-        CHECK2{{"Result check"}}
+        CHECK2{{"Result check / 结果检查"}}
-        SUCCESS["Ok(contents)"]
+        SUCCESS["Ok(contents) / 成功"]
-        ERROR1["Err(io::Error)"]
+        ERROR1["Err(io::Error) / 错误"]
-        ERROR2["Err(io::Error)"]
+        ERROR2["Err(io::Error) / 错误"]
         
         START --> OP1
         OP1 --> CHECK1
-        CHECK1 -->|"Ok(file)"| OP2
+        CHECK1 -->|"Ok(file) / 成功"| OP2
-        CHECK1 -->|"Err(e)"| ERROR1
+        CHECK1 -->|"Err(e) / 失败"| ERROR1
         OP2 --> CHECK2
-        CHECK2 -->|"Ok(())"| SUCCESS
+        CHECK2 -->|"Ok(()) / 成功"| SUCCESS
-        CHECK2 -->|"Err(e)"| ERROR2
+        CHECK2 -->|"Err(e) / 失败"| ERROR2
         
-        ERROR1 --> PROPAGATE["? operator<br/>propagates error"]
+        ERROR1 --> PROPAGATE["? operator / 运算符<br/>propagates error / 传播错误"]
         ERROR2 --> PROPAGATE
-        PROPAGATE --> CALLER["Caller must<br/>handle error"]
+        PROPAGATE --> CALLER["Caller must / 调用者必须<br/>handle error / 处理错误"]
     end
     
-    subgraph "Pattern Matching Options"
+    subgraph "Pattern Matching Options / 模式匹配选项"
-        MATCH["match result"]
+        MATCH["match result / 匹配结果"]
-        IFLET["if let Ok(val) = result"]
+        IFLET["if let Ok(val) = result / 条件赋值"]
-        UNWRAP["result.unwrap()<br/>[WARNING] Panics on error"]
+        UNWRAP["result.unwrap()<br/>[WARNING] Panics on error / 报错会引发 Panic"]
-        EXPECT["result.expect(msg)<br/>[WARNING] Panics with message"]
+        EXPECT["result.expect(msg)<br/>[WARNING] Panics with message / 报错并带自定义信息"]
-        UNWRAP_OR["result.unwrap_or(default)<br/>[OK] Safe fallback"]
+        UNWRAP_OR["result.unwrap_or(default)<br/>[OK] Safe fallback / 安全备选项"]
-        QUESTION["result?<br/>[OK] Early return"]
+        QUESTION["result?<br/>[OK] Early return / 提早返回"]
         
-        MATCH --> SAFE1["[OK] Handles both cases"]
+        MATCH --> SAFE1["[OK] Handles both cases / 处理两种情况"]
-        IFLET --> SAFE2["[OK] Handles error case"]
+        IFLET --> SAFE2["[OK] Handles error case / 处理错误情况"]
-        UNWRAP_OR --> SAFE3["[OK] Always returns value"]
+        UNWRAP_OR --> SAFE3["[OK] Always returns value / 始终返回值"]
-        QUESTION --> SAFE4["[OK] Propagates to caller"]
+        QUESTION --> SAFE4["[OK] Propagates to caller / 传播给调用者"]
-        UNWRAP --> UNSAFE1["[ERROR] Can panic"]
+        UNWRAP --> UNSAFE1["[ERROR] Can panic / 可能 Panic"]
-        EXPECT --> UNSAFE2["[ERROR] Can panic"]
+        EXPECT --> UNSAFE2["[ERROR] Can panic / 可能 Panic"]
     end
     
     style SUCCESS fill:#91e5a3,color:#000
     style ERROR1 fill:#ffa07a,color:#000
     style ERROR2 fill:#ffa07a,color:#000
     style SAFE1 fill:#91e5a3,color:#000
     style SAFE2 fill:#91e5a3,color:#000
     style SAFE3 fill:#91e5a3,color:#000
     style SAFE4 fill:#91e5a3,color:#000
     style UNSAFE1 fill:#ff6b6b,color:#000
     style UNSAFE2 fill:#ff6b6b,color:#000
    • Rust uses the enum Result<T, E> enum for recoverable error handling
    • Rust 使用 enum Result<T, E> 枚举进行可恢复的错误处理
  • - The ```Ok<T>``` variant contains the result in case of success and ```Err<E>``` contains the error
    
  • - ```Ok<T>``` 变体包含成功时的结果,而 ```Err<E>``` 包含错误。
    
fn main() {
    let x = "1234x".parse::<u32>();
    match x {
        Ok(x) => println!("Parsed number {x}"),
        Err(e) => println!("Parsing error {e:?}"),
    }
    let x  = "1234".parse::<u32>();
-    // Same as above, but with valid number
+    // Same as above, but with valid number / 与上面相同,但使用了有效数字
    if let Ok(x) = &x {
        println!("Parsed number {x}")
    } else if let Err(e) = &x {
        println!("Error: {e:?}");
    }
}
    • The try-operator ? is a convenient short hand for the match Ok / Err pattern
    • 问号操作符 ?match Ok / Err 模式的便捷简写
  • - Note the method must return ```Result<T, E>``` to enable use of ```?```
    
  • - 注意:方法必须返回 ```Result<T, E>``` 才能启用 ```?``` 的使用。
    
  • - The type for ```Result<T, E>``` can be changed. In the example below, we return the same error type (```std::num::ParseIntError```) returned by ```str::parse()``` 
    
  • - ```Result<T, E>``` 的类型可以更改。在下面的示例中,我们返回由 ```str::parse()``` 返回的相同错误类型(```std::num::ParseIntError```)。
    
fn double_string_number(s : &str) -> Result<u32, std::num::ParseIntError> {
-   let x = s.parse::<u32>()?; // Returns immediately in case of an error
+   let x = s.parse::<u32>()?; // Returns immediately in case of an error / 如遇错误立即返回
   Ok(x*2)
}
fn main() {
    let result = double_string_number("1234");
    println!("{result:?}");
    let result = double_string_number("1234x");
    println!("{result:?}");
}
    • Errors can be mapped to other types, or to default values (https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap_or_default)
    • 错误可以映射到其他类型,或映射到默认值(https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap_or_default)
#![allow(unused)]
fn main() {
-// Changes the error type to () in case of error
+// Changes the error type to () in case of error / 如果出错,将错误类型更改为 ()
fn double_string_number(s : &str) -> Result<u32, ()> {
-   let x = s.parse::<u32>().map_err(|_|())?; // Returns immediately in case of an error
+   let x = s.parse::<u32>().map_err(|_|())?; // Returns immediately in case of an error / 如果出错立即返回
   Ok(x*2)
}
}
#![allow(unused)]
fn main() {
fn double_string_number(s : &str) -> Result<u32, ()> {
-   let x = s.parse::<u32>().unwrap_or_default(); // Defaults to 0 in case of parse error
+   let x = s.parse::<u32>().unwrap_or_default(); // Defaults to 0 / 在解析错误时默认为 0
   Ok(x*2)
}
}
#![allow(unused)]
fn main() {
fn double_optional_number(x : Option<u32>) -> Result<u32, ()> {
-    // ok_or converts Option<None> to Result<u32, ()> in the below
+    // ok_or converts Option<None> to Result<u32, ()> / 下面 ok_or 将 Option<None> 转换为 Result<u32, ()>
-    x.ok_or(()).map(|x|x*2) // .map() is applied only on Ok(u32)
+    x.ok_or(()).map(|x|x*2) // .map() is applied only on Ok(u32) / .map() 仅应用于 Ok(u32)
}
}
  • 🟡 Intermediate
  • 🟡 Intermediate / 中级
    • Implement a log() function with a single u32 parameter. If the parameter is not 42, return an error. The Result<> for success and error type is ()
    • 实现一个带有单个 u32 参数的 log() 函数。如果参数不是 42,则返回错误。成功和错误的 Result<> 类型均为 ()
    • Invoke log() function that exits with the same Result<> type if log() return an error. Otherwise print a message saying that log was successfully called
    • 调用 log() 函数,如果 log() 返回错误,则以相同的 Result<> 类型退出。否则打印一条消息,说明 log 调用成功。
fn log(x: u32) -> ?? {

}

fn call_log(x: u32) -> ?? {
-    // Call log(x), then exit immediately if it return an error
+    // Call log(x), then exit immediately if it return an error / 调用 log(x),如果它返回错误则立即退出
-    println!("log was successfully called");
+    println!("log was successfully called / log 调用成功");
}

fn main() {
    call_log(42);
    call_log(43);
}
  • Solution (click to expand)
  • Solution (click to expand) / 解决方案(点击展开)
fn log(x: u32) -> Result<(), ()> {
    if x == 42 {
        Ok(())
    } else {
        Err(())
    }
}

fn call_log(x: u32) -> Result<(), ()> {
-    log(x)?;  // Exit immediately if log() returns an error
+    log(x)?;  // Exit immediately if log() returns an error / 如果 log() 返回错误则立即退出
-    println!("log was successfully called with {x}");
+    println!("log was successfully called with {x} / 已成功调用 log,值为 {x}");
    Ok(())
}

fn main() {
-    let _ = call_log(42);  // Prints: log was successfully called with 42
+    let _ = call_log(42);  // Prints: log was successfully called with 42 / 打印成功信息
-    let _ = call_log(43);  // Returns Err(()), nothing printed
+    let _ = call_log(43);  // Returns Err(()), nothing printed / 返回 Err(()),没有任何打印
}
- // Output:
+ // Output / 输出:
// log was successfully called with 42

Rust Option and Result key takeaways / Rust Option 与 Result 核心要点

What you’ll learn / 你将学到: Idiomatic error handling patterns — safe alternatives to unwrap(), the ? operator for propagation, custom error types, and when to use anyhow vs thiserror in production code.

地道的错误处理模式 —— unwrap() 的安全替代方案、用于错误传播的 ? 运算符、自定义错误类型,以及在生产代码中何时使用 anyhowthiserror

  • Option and Result are an integral part of idiomatic Rust / OptionResult 是地道 Rust 代码中不可或缺的一部分
    • Safe alternatives to unwrap():
    • Safe alternatives to unwrap() / unwrap() 的安全替代方案
#![allow(unused)]
fn main() {
-// Option<T> safe alternatives
+// Option<T> safe alternatives / Option<T> 的安全替代方案
- let value = opt.unwrap_or(default);              // Provide fallback value
+ let value = opt.unwrap_or(default);              // Provide fallback value / 提供备选值
- let value = opt.unwrap_or_else(|| compute());    // Lazy computation for fallback
+ let value = opt.unwrap_or_else(|| compute());    // Lazy computation / 惰性计算备选值
- let value = opt.unwrap_or_default();             // Use Default trait implementation
+ let value = opt.unwrap_or_default();             // Use Default trait / 使用 Default trait 的实现
- let value = opt.expect("descriptive message");   // Only when panic is acceptable
+ let value = opt.expect("descriptive message");   // Only when panic is acceptable / 仅当允许 panic 时使用,带描述信息

-// Result<T, E> safe alternatives  
+// Result<T, E> safe alternatives / Result<T, E> 的安全替代方案
- let value = result.unwrap_or(fallback);          // Ignore error, use fallback
+ let value = result.unwrap_or(fallback);          // Ignore error / 忽略错误,使用备选值
- let value = result.unwrap_or_else(|e| handle(e)); // Handle error, return fallback
+ let value = result.unwrap_or_else(|e| handle(e)); // Handle error / 处理错误并返回备选值
- let value = result.unwrap_or_default();          // Use Default trait
+ let value = result.unwrap_or_default();          // Use Default trait / 使用 Default trait
}
    • Pattern matching for explicit control:
    • Pattern matching for explicit control / 用于显式控制的模式匹配
#![allow(unused)]
fn main() {
match some_option {
-    Some(value) => println!("Got: {}", value),
+    Some(value) => println!("Got: {}", value), // 匹配到值
-    None => println!("No value found"),
+    None => println!("No value found"),       // 无值
}

match some_result {
-    Ok(value) => process(value),
+    Ok(value) => process(value),     // 成功
-    Err(error) => log_error(error),
+    Err(error) => log_error(error),  // 错误
}
}
    • Use ? operator for error propagation: Short-circuit and bubble up errors
    • Use ? operator for error propagation / 使用 ? 运算符进行错误传播:短路并向上抛出错误
#![allow(unused)]
fn main() {
fn process_file(path: &str) -> Result<String, std::io::Error> {
-    let content = std::fs::read_to_string(path)?; // Automatically returns error
+    let content = std::fs::read_to_string(path)?; // Automatically returns error / 自动返回错误
    Ok(content.to_uppercase())
}
}
    • Transformation methods:
    • Transformation methods / 转换方法
  • - `map()`: Transform the success value `Ok(T)` -> `Ok(U)` or `Some(T)` -> `Some(U)`
    
  • - `map()`:转换成功值 `Ok(T)` -> `Ok(U)` 或 `Some(T)` -> `Some(U)`
    
  • - `map_err()`: Transform the error type `Err(E)` -> `Err(F)`
    
  • - `map_err()`:转换错误类型 `Err(E)` -> `Err(F)`
    
  • - `and_then()`: Chain operations that can fail
    
  • - `and_then()`:链接可能失败的操作
    
    • Use in your own APIs: Prefer Result<T, E> over exceptions or error codes
    • Use in your own APIs / 在你自己的 API 中使用:优先使用 Result<T, E> 而非异常或错误码
    • Borrowing issues: Most common beginner mistake
    • Borrowing issues / 借用问题:最常见的初学者错误
  • - "cannot borrow as mutable" -> Only one mutable reference allowed at a time
    
  • - "cannot borrow as mutable" -> 同一时间只允许一个可变引用
    
  • - "borrowed value does not live long enough" -> Reference outlives the data it points to
    
  • - "borrowed value does not live long enough" -> 引用的存活时间超过了它指向的数据
    
  • - **Fix**: Use scopes `{}` to limit reference lifetimes, or clone data when needed
    
  • - **Fix / 修复**:使用作用域 `{}` 限制引用生命周期,或在需要时克隆数据
    
    • Missing trait implementations: “method not found” errors
    • Missing trait implementations / 缺失 Trait 实现:“method not found” 错误
  • - **Fix**: Add `#[derive(Debug, Clone, PartialEq)]` for common traits
    
  • - **Fix / 修复**:为常用 trait 添加 `#[derive(Debug, Clone, PartialEq)]`
    
  • - Use `cargo check` to get better error messages than `cargo run`
    
  • - 使用 `cargo check` 获得比 `cargo run` 更好的错误消息
    
    • Integer overflow in debug mode: Rust panics on overflow
    • Integer overflow in debug mode / 调试模式下的整数溢出:Rust 在溢出时会发生 panic
  • - **Fix**: Use `wrapping_add()`, `saturating_add()`, or `checked_add()` for explicit behavior
    
  • - **Fix / 修复**:使用 `wrapping_add()`、`saturating_add()` 或 `checked_add()` 来指定明确的行为
    
    • String vs &str confusion: Different types for different use cases
    • String vs &str confusion / String 与 &str 的混淆:不同用例使用不同类型
  • - Use `&str` for string slices (borrowed), `String` for owned strings
    
  • - `&str` 用于字符串切片(借用),`String` 用于拥有所有权的字符串
    
  • - **Fix**: Use `.to_string()` or `String::from()` to convert `&str` to `String`
    
  • - **Fix / 修复**:使用 `.to_string()` 或 `String::from()` 将 `&str` 转换为 `String`
    
    • Fighting the borrow checker: Don’t try to outsmart it
    • Fighting the borrow checker / 与借用检查器对抗:不要试图自作聪明
  • - **Fix**: Restructure code to work with ownership rules rather than against them
    
  • - **Fix / 修复**:重构代码以适应所有权规则,而不是违背它们
    
  • - Consider using `Rc<RefCell<T>>` for complex sharing scenarios (sparingly)
    
  • - 对于复杂的共享场景,考虑(谨慎地)使用 `Rc<RefCell<T>>`
    
#![allow(unused)]
fn main() {
- // [ERROR] BAD: Can panic unexpectedly
+ // [ERROR] BAD: Can panic unexpectedly / [劣] 可能意外触发 Panic
fn bad_config_reader() -> String {
-    let config = std::env::var("CONFIG_FILE").unwrap(); // Panic if not set!
+    let config = std::env::var("CONFIG_FILE").unwrap(); // Panic if not set! / 未设置则 Panic!
-    std::fs::read_to_string(config).unwrap()           // Panic if file missing!
+    std::fs::read_to_string(config).unwrap()           // Panic if file missing! / 文件缺失则 Panic!
}

- // [OK] GOOD: Handles errors gracefully
+ // [OK] GOOD: Handles errors gracefully / [优] 优雅处理错误
fn good_config_reader() -> Result<String, ConfigError> {
    let config_path = std::env::var("CONFIG_FILE")
-        .unwrap_or_else(|_| "default.conf".to_string()); // Fallback to default
+        .unwrap_or_else(|_| "default.conf".to_string()); // Fallback / 备选默认值
    
    let content = std::fs::read_to_string(config_path)
-        .map_err(ConfigError::FileRead)?;                // Convert and propagate error
+        .map_err(ConfigError::FileRead)?;                // Convert and propagate / 转换并传播错误
    
    Ok(content)
}

- // [OK] EVEN BETTER: With proper error types
+ // [OK] EVEN BETTER: With proper error types / [更佳] 使用适当的错误类型
use thiserror::Error;

#[derive(Error, Debug)]
enum ConfigError {
    #[error("Failed to read config file: {0}")]
    FileRead(#[from] std::io::Error),
    
    #[error("Invalid configuration: {message}")]
    Invalid { message: String },
}
}
  • Let’s break down what’s happening here. ConfigError has just two variants — one for I/O errors and one for validation errors. This is the right starting point for most modules:
  • 让我们分析一下这里发生了什么。ConfigError 只有两个变体 —— 一个用于 I/O 错误,另一个用于验证错误。这是大多数模块的正确起点:

-| ConfigError variant | Holds | Created by | +| ConfigError variant / 变体 | Holds / 持有内容 | Created by / 创建者 | |–––––––––––|—––|———–| -| FileRead(io::Error) | The original I/O error | #[from] auto-converts via ? | +| FileRead(io::Error) | The original I/O error / 原始 I/O 错误 | #[from] auto-converts via ? / 通过 ? 自动转换 | -| Invalid { message } | A human-readable explanation | Your validation code | +| Invalid { message } | A human-readable explanation / 人类可读的解释 | Your validation code / 你的验证代码 |

  • Now you can Write functions that return Result<T, ConfigError>:
  • 现在你可以编写返回 Result<T, ConfigError> 的函数了:
#![allow(unused)]
fn main() {
fn read_config(path: &str) -> Result<String, ConfigError> {
-    let content = std::fs::read_to_string(path)?;  // io::Error → ConfigError::FileRead
+    let content = std::fs::read_to_string(path)?;  // io::Error → ConfigError::FileRead / 自动转换
    if content.is_empty() {
        return Err(ConfigError::Invalid {
            message: "config file is empty".to_string(),
        });
    }
    Ok(content)
}
}
  • 🟢 Self-study checkpoint: Before continuing, make sure you can answer:

  • 🟢 Self-study checkpoint / 自学检查点:在继续之前,请确保你能回答:

    1. Why does ? on the read_to_string call work? (Because #[from] generates impl From<io::Error> for ConfigError)
    1. 为什么 read_to_string 调用上的 ? 能奏效?(因为 #[from] 生成了 impl From<io::Error> for ConfigError
    1. What happens if you add a third variant MissingKey(String) — what code changes? (Just add the variant; existing code still compiles)
    1. 如果你添加了第三个变体 MissingKey(String),会发生什么 —— 哪些代码需要更改?(只需加上变体;现有代码依然可以编译)
  • As your project grows beyond a single file, you’ll combine multiple module-level errors into a crate-level error type. This is the standard pattern in production Rust. Let’s build up from the ConfigError above.
  • 随着项目规模超过单个文件,你将把多个模块级错误组合成一个 crate 级错误类型。这是生产级 Rust 中的标准模式。让我们在上面的 ConfigError 基础上继续构建。
  • In real-world Rust projects, every crate (or significant module) defines its own Error
  • 在现实世界的 Rust 项目中,每个 crate(或重要模块)都会定义自己的 Error
  • enum and a Result type alias. This is the idiomatic pattern — analogous to how in C++
  • 枚举和一个 Result 类型别名。这是地道的模式 —— 类似于在 C++ 中
  • you’d define a per-library exception hierarchy and using Result = std::expected<T, Error>.
  • 你会为每个库定义异常层次结构以及 using Result = std::expected<T, Error>
#![allow(unused)]
fn main() {
- // src/error.rs  (or at the top of lib.rs)
+ // src/error.rs  (或者在 lib.rs 的顶部)
use thiserror::Error;

- /// Every error this crate can produce.
+ /// Every error this crate can produce / 本 crate 可能产生的所有错误
#[derive(Error, Debug)]
pub enum Error {
    #[error("I/O error: {0}")]
-    Io(#[from] std::io::Error),          // auto-converts via From
+    Io(#[from] std::io::Error),          // auto-converts / 通过 From 自动转换

    #[error("JSON parse error: {0}")]
-    Json(#[from] serde_json::Error),     // auto-converts via From
+    Json(#[from] serde_json::Error),     // auto-converts / 通过 From 自动转换

    #[error("Invalid sensor id: {0}")]
-    InvalidSensor(u32),                  // domain-specific variant
+    InvalidSensor(u32),                  // domain-specific / 领域特定变体

    #[error("Timeout after {ms} ms")]
    Timeout { ms: u64 },
}

- /// Crate-wide Result alias — saves typing throughout the crate.
+ /// Crate-wide Result alias / Crate 范围的 Result 别名 —— 减少整个 crate 中的输入
pub type Result<T> = core::result::Result<T, Error>;
}
  • Without the alias you’d write:
  • 如果没有别名,你会写:
#![allow(unused)]
fn main() {
- // Verbose — error type repeated everywhere
+ // Verbose / 冗长 —— 错误类型到处重复
fn read_sensor(id: u32) -> Result<f64, crate::Error> { ... }
fn parse_config(path: &str) -> Result<Config, crate::Error> { ... }
}
  • With the alias:
  • 有了别名:
#![allow(unused)]
fn main() {
- // Clean — just `Result<T>`
+ // Clean / 简洁 —— 只需 `Result<T>`
use crate::{Error, Result};

fn read_sensor(id: u32) -> Result<f64> {
    if id > 128 {
        return Err(Error::InvalidSensor(id));
    }
-    let raw = std::fs::read_to_string(format!("/dev/sensor/{id}"))?; // io::Error → Error::Io
+    let raw = std::fs::read_to_string(format!("/dev/sensor/{id}"))?; // 自动转换:io::Error → Error::Io
    let value: f64 = raw.trim().parse()
-        .map_err(|_| Error::InvalidSensor(id))?;
+        .map_err(|_| Error::InvalidSensor(id))?; // 手动映射错误
    Ok(value)
}
}
  • The #[from] attribute on Io generates this impl for free:
  • Io 上的 #[from] 属性免费生成了以下 impl
#![allow(unused)]
fn main() {
- // Auto-generated by thiserror's #[from]
+ // 由 thiserror 的 #[from] 自动生成
impl From<std::io::Error> for Error {
    fn from(source: std::io::Error) -> Self {
        Error::Io(source)
    }
}
}
  • That’s what makes ? work: when a function returns std::io::Error and your function
  • 这正是 ? 奏效的原因:当一个函数返回 std::io::Error 而你的函数
  • returns Result<T> (your alias), the compiler calls From::from() to convert it
  • 返回 Result<T>(你的别名)时,编译器会自动调用 From::from() 进行转换。
  • Larger crates split errors by module, then compose them at the crate root:
  • 较大的 crate 会按模块拆分错误,然后在 crate 根部进行组合:
#![allow(unused)]
fn main() {
// src/config/error.rs
#[derive(thiserror::Error, Debug)]
pub enum ConfigError {
    #[error("Missing key: {0}")]
    MissingKey(String),
    #[error("Invalid value for '{key}': {reason}")]
    InvalidValue { key: String, reason: String },
}

- // src/error.rs  (crate-level)
+ // src/error.rs  (crate 级)
#[derive(thiserror::Error, Debug)]
pub enum Error {
-    #[error(transparent)]               // delegates Display to inner error
+    #[error(transparent)]               // delegates Display / 将 Display 委托给内部错误
    Config(#[from] crate::config::ConfigError),

    #[error("I/O error: {0}")]
    Io(#[from] std::io::Error),
}
pub type Result<T> = core::result::Result<T, Error>;
}
  • Callers can still match on specific config errors:
  • 调用者仍然可以对特定的配置错误进行匹配:
#![allow(unused)]
fn main() {
match result {
-    Err(Error::Config(ConfigError::MissingKey(k))) => eprintln!("Add '{k}' to config"),
+    Err(Error::Config(ConfigError::MissingKey(k))) => eprintln!("Add '{k}' to config / 请在配置中添加 '{k}'"),
-    Err(e) => eprintln!("Other error: {e}"),
+    Err(e) => eprintln!("Other error: {e} / 其他错误:{e}"),
    Ok(v) => use_value(v),
}
}

-| Concept | C++ | Rust | +| Concept / 概念 | C++ | Rust | |———|—–|——| -| Error hierarchy | class AppError : public std::runtime_error | #[derive(thiserror::Error)] enum Error { ... } | +| Error hierarchy / 错误层次 | class AppError : public std::runtime_error | #[derive(thiserror::Error)] enum Error { ... } | -| Return error | std::expected<T, Error> or throw | fn foo() -> Result<T> | +| Return error / 返回错误 | std::expected<T, Error> or throw | fn foo() -> Result<T> | -| Convert error | Manual try/catch + rethrow | #[from] + ? — zero boilerplate / 零样板代码 | +| Convert error / 转换错误 | Manual try/catch + rethrow | #[from] + ? | -| Result alias | template<class T> using Result = std::expected<T, Error>; | pub type Result<T> = core::result::Result<T, Error>; | +| Result alias / 结果别名 | template<class T> ... | pub type Result<T> = ... | -| Error message | Override what() | #[error("...")] — compiled into Display impl | +| Error message / 错误消息 | Override what() | #[error("...")] — 编译为 Display 实现 |

Rust traits / Rust Trait

What you’ll learn / 你将学到: Traits — Rust’s answer to interfaces, abstract base classes, and operator overloading. You’ll learn how to define traits, implement them for your types, and use dynamic dispatch (dyn Trait) vs static dispatch (generics). For C++ developers: traits replace virtual functions, CRTP, and concepts. For C developers: traits are the structured way Rust does polymorphism.

Trait —— Rust 对接口、抽象基类和运算符重载的回答。你将学习如何定义 trait、为你的类型实现它们,以及如何使用动态分派(dyn Trait)与静态分派(泛型)。对于 C++ 开发者:trait 取代了虚函数、CRTP 和 concepts。对于 C 开发者:trait 是 Rust 实现多态的有组织方式。

  • Rust traits are similar to interfaces in other languages / Rust trait 类似于其他语言中的接口
  • - Traits define methods that must be defined by types that implement the trait.
    
  • - Traits define methods that must be defined by types that implement the trait. / Trait 定义了实现该 trait 的类型必须定义的方法。
    
fn main() {
    trait Pet {
        fn speak(&self);
    }
    struct Cat;
    struct Dog;
    impl Pet for Cat {
        fn speak(&self) {
            println!("Meow");
        }
    }
    impl Pet for Dog {
        fn speak(&self) {
            println!("Woof!")
        }
    }
    let c = Cat{};
    let d = Dog{};
-    c.speak();  // There is no "is a" relationship between Cat and Dog
+    c.speak();  // There is no "is a" relationship between Cat and Dog / Cat 和 Dog 之间没有“是一个(is a)”的关系
-    d.speak(); // There is no "is a" relationship between Cat and Dog
+    d.speak(); // There is no "is a" relationship between Cat and Dog / Cat 和 Dog 之间没有“是一个(is a)”的关系
}
// C++ - Inheritance-based polymorphism
+// C++ - 基于继承的多态
class Animal {
public:
-    virtual void speak() = 0;  // Pure virtual function
+    virtual void speak() = 0;  // Pure virtual function / 纯虚函数
    virtual ~Animal() = default;
};

- class Cat : public Animal {  // "Cat IS-A Animal"
+ class Cat : public Animal {  // "Cat IS-A Animal" / “Cat 是一个 Animal”
public:
    void speak() override {
        std::cout << "Meow" << std::endl;
    }
};

- void make_sound(Animal* animal) {  // Runtime polymorphism
+ void make_sound(Animal* animal) {  // Runtime polymorphism / 运行时多态
-    animal->speak();  // Virtual function call
+    animal->speak();  // Virtual function call / 虚函数调用
}
#![allow(unused)]
fn main() {
// Rust - Composition over inheritance with traits
+// Rust - 通过 trait 实现组合优于继承
trait Animal {
    fn speak(&self);
}

- struct Cat;  // Cat is NOT an Animal, but IMPLEMENTS Animal behavior
+ struct Cat;  // Cat is NOT an Animal, but IMPLEMENTS Animal behavior / Cat 不是 Animal,但实现了 Animal 的行为

- impl Animal for Cat {  // "Cat CAN-DO Animal behavior"
+ impl Animal for Cat {  // "Cat CAN-DO Animal behavior" / “Cat 可以执行 Animal 的行为”
    fn speak(&self) {
        println!("Meow");
    }
}

- fn make_sound<T: Animal>(animal: &T) {  // Static polymorphism
+ fn make_sound<T: Animal>(animal: &T) {  // Static polymorphism / 静态多态
-    animal.speak();  // Direct function call (zero cost)
+    animal.speak();  // Direct function call (zero cost) / 直接函数调用(零成本)
}
}
graph TD
-    subgraph "C++ Object-Oriented Hierarchy"
+    subgraph "C++ Object-Oriented Hierarchy / 面向对象层次结构"
-        CPP_ANIMAL["Animal<br/>(Abstract base class)"]
+        CPP_ANIMAL["Animal / 接口<br/>(Abstract base class / 抽象基类)"]
-        CPP_CAT["Cat : public Animal<br/>(IS-A relationship)"]
+        CPP_CAT["Cat : public Animal<br/>(IS-A relationship / “是一个”关系)"]
-        CPP_DOG["Dog : public Animal<br/>(IS-A relationship)"]
+        CPP_DOG["Dog : public Animal<br/>(IS-A relationship / “是一个”关系)"]
         
         CPP_ANIMAL --> CPP_CAT
         CPP_ANIMAL --> CPP_DOG
         
-        CPP_VTABLE["Virtual function table<br/>(Runtime dispatch)"]
+        CPP_VTABLE["Virtual function table / 虚函数表<br/>(Runtime dispatch / 运行时分派)"]
-        CPP_HEAP["Often requires<br/>heap allocation"]
+        CPP_HEAP["Often requires / 通常需要<br/>heap allocation / 堆分配"]
-        CPP_ISSUES["[ERROR] Deep inheritance trees<br/>[ERROR] Diamond problem<br/>[ERROR] Runtime overhead<br/>[ERROR] Tight coupling"]
+        CPP_ISSUES["[ERROR] Deep inheritance trees / 深层继承树<br/>[ERROR] Diamond problem / 菱形继承问题<br/>[ERROR] Runtime overhead / 运行时开销<br/>[ERROR] Tight coupling / 紧耦合"]
     end
     
-    subgraph "Rust Trait-Based Composition"
+    subgraph "Rust Trait-Based Composition / 基于 Trait 的组合"
-        RUST_TRAIT["trait Animal<br/>(Behavior definition)"]
+        RUST_TRAIT["trait Animal / 行为定义<br/>(Behavior definition)"]
-        RUST_CAT["struct Cat<br/>(Data only)"]
+        RUST_CAT["struct Cat<br/>(Data only / 仅数据)"]
-        RUST_DOG["struct Dog<br/>(Data only)"]
+        RUST_DOG["struct Dog<br/>(Data only / 仅数据)"]
         
-        RUST_CAT -.->|"impl Animal for Cat<br/>(CAN-DO behavior)"| RUST_TRAIT
+        RUST_CAT -.->|"impl Animal for Cat<br/>(CAN-DO behavior / “可以执行”行为)"| RUST_TRAIT
-        RUST_DOG -.->|"impl Animal for Dog<br/>(CAN-DO behavior)"| RUST_TRAIT
+        RUST_DOG -.->|"impl Animal for Dog<br/>(CAN-DO behavior / “可以执行”行为)"| RUST_TRAIT
         
-        RUST_STATIC["Static dispatch<br/>(Compile-time)"]
+        RUST_STATIC["Static dispatch / 静态分派<br/>(Compile-time / 编译时)"]
-        RUST_STACK["Stack allocation<br/>possible"]
+        RUST_STACK["Stack allocation / 栈分配<br/>possible / 是可能的"]
-        RUST_BENEFITS["[OK] No inheritance hierarchy<br/>[OK] Multiple trait impls<br/>[OK] Zero runtime cost<br/>[OK] Loose coupling"]
+        RUST_BENEFITS["[OK] No inheritance hierarchy / 无继承层次<br/>[OK] Multiple trait impls / 多个 trait 实现<br/>[OK] Zero runtime cost / 零运行时成本<br/>[OK] Loose coupling / 松耦合"]
     end
     
     style CPP_ISSUES fill:#ff6b6b,color:#000
     style RUST_BENEFITS fill:#91e5a3,color:#000
     style CPP_VTABLE fill:#ffa07a,color:#000
     style RUST_STATIC fill:#91e5a3,color:#000
#![allow(unused)]
fn main() {
use std::fmt::Display;
use std::ops::Add;

- // C++ template equivalent (less constrained)
+ // C++ template equivalent (less constrained) / C++ 模板等价物(约束较少)
// template<typename T>
// T add_and_print(T a, T b) {
- //     // No guarantee T supports + or printing
+ //     // No guarantee T supports + or printing / 无法保证 T 支持 + 或打印
- //     return a + b;  // Might fail at compile time
+ //     return a + b;  // Might fail at compile time / 可能会在编译时失败
// }

- // Rust - explicit trait bounds
+ // Rust - explicit trait bounds / Rust - 显式的 trait 限定
fn add_and_print<T>(a: T, b: T) -> T 
where 
    T: Display + Add<Output = T> + Copy,
{
-    println!("Adding {} + {}", a, b);  // Display trait
+    println!("Adding {} + {}", a, b);  // Display trait / Display trait 支持
-    a + b  // Add trait
+    a + b  // Add trait / Add trait 支持
}
}
graph TD
-    subgraph "Generic Constraints Evolution"
+    subgraph "Generic Constraints Evolution / 泛型约束的演进"
-        UNCONSTRAINED["fn process<T>(data: T)<br/>[ERROR] T can be anything"]
+        UNCONSTRAINED["fn process<T>(data: T)<br/>[ERROR] T can be anything / T 可以是任何东西"]
-        SINGLE_BOUND["fn process<T: Display>(data: T)<br/>[OK] T must implement Display"]
+        SINGLE_BOUND["fn process<T: Display>(data: T)<br/>[OK] T must implement Display / T 必须实现 Display"]
-        MULTI_BOUND["fn process<T>(data: T)<br/>where T: Display + Clone + Debug<br/>[OK] Multiple requirements"]
+        MULTI_BOUND["fn process<T>(data: T)<br/>where T: Display + Clone + Debug<br/>[OK] Multiple requirements / 多重需求"]
         
         UNCONSTRAINED --> SINGLE_BOUND
         SINGLE_BOUND --> MULTI_BOUND
     end
     
-    subgraph "Trait Bound Syntax"
+    subgraph "Trait Bound Syntax / Trait 限定语法"
-        INLINE["fn func<T: Trait>(param: T)"]
+        INLINE["fn func<T: Trait>(param: T) / 内联"]
-        WHERE_CLAUSE["fn func<T>(param: T)<br/>where T: Trait"]
+        WHERE_CLAUSE["fn func<T>(param: T)<br/>where T: Trait / Where 子句"]
-        IMPL_PARAM["fn func(param: impl Trait)"]
+        IMPL_PARAM["fn func(param: impl Trait) / impl 参数"]
         
-        COMPARISON["Inline: Simple cases<br/>Where: Complex bounds<br/>impl: Concise syntax"]
+        COMPARISON["Inline: Simple cases / 简单场景<br/>Where: Complex bounds / 复杂限定<br/>impl: Concise syntax / 简洁语法"]
     end
     
-    subgraph "Compile-time Magic"
+    subgraph "Compile-time Magic / 编译时魔法"
-        GENERIC_FUNC["Generic function<br/>with trait bounds"]
+        GENERIC_FUNC["Generic function / 泛型函数<br/>with trait bounds / 带有 trait 限定"]
-        TYPE_CHECK["Compiler verifies<br/>trait implementations"]
+        TYPE_CHECK["Compiler verifies / 编译器验证<br/>trait implementations / trait 实现"]
-        MONOMORPH["Monomorphization<br/>(Create specialized versions)"]
+        MONOMORPH["Monomorphization / 单态化<br/>(Create specialized versions / 创建专用版本)"]
-        OPTIMIZED["Fully optimized<br/>machine code"]
+        OPTIMIZED["Fully optimized / 完全优化<br/>machine code / 机器码"]
         
         GENERIC_FUNC --> TYPE_CHECK
         TYPE_CHECK --> MONOMORPH
         MONOMORPH --> OPTIMIZED
         
-        EXAMPLE["add_and_print::<i32><br/>add_and_print::<f64><br/>(Separate functions generated)"]
+        EXAMPLE["add_and_print::<i32><br/>add_and_print::<f64><br/>(Separate functions generated / 生成了独立的函数)"]
         MONOMORPH --> EXAMPLE
     end
     
     style UNCONSTRAINED fill:#ff6b6b,color:#000
     style SINGLE_BOUND fill:#ffa07a,color:#000
     style MULTI_BOUND fill:#91e5a3,color:#000
     style OPTIMIZED fill:#91e5a3,color:#000
  • In C++, you overload operators by writing free functions or member functions with special names (operator+, operator<<, operator[], etc.). In Rust, every operator maps to a trait in std::ops (or std::fmt for output). You implement the trait instead of writing a magic-named function.
  • 在 C++ 中,你通过编写具有特殊名称(operator+operator<<operator[] 等)的全局函数或成员函数来重载运算符。在 Rust 中,每个运算符都映射到 std::ops(或用于输出的 std::fmt)中的一个 trait。你实现该 trait,而不是编写具有魔法名称的函数。
// C++: operator overloading as a member or free function
+// C++:作为成员或全局函数的运算符重载
struct Vec2 {
    double x, y;
    Vec2 operator+(const Vec2& rhs) const {
        return {x + rhs.x, y + rhs.y};
    }
};

Vec2 a{1.0, 2.0}, b{3.0, 4.0};
- Vec2 c = a + b;  // calls a.operator+(b)
+ Vec2 c = a + b;  // calls / 调用 a.operator+(b)
#![allow(unused)]
fn main() {
use std::ops::Add;

#[derive(Debug, Clone, Copy)]
struct Vec2 { x: f64, y: f64 }

impl Add for Vec2 {
-    type Output = Vec2;                     // Associated type — the result of +
+    type Output = Vec2;                     // Associated type / 关联类型 —— + 的结果
    fn add(self, rhs: Vec2) -> Vec2 {
        Vec2 { x: self.x + rhs.x, y: self.y + rhs.y }
    }
}

let a = Vec2 { x: 1.0, y: 2.0 };
let b = Vec2 { x: 3.0, y: 4.0 };
- let c = a + b;  // calls <Vec2 as Add>::add(a, b)
+ let c = a + b;  // calls / 调用 <Vec2 as Add>::add(a, b)
- println!("{c:?}"); // Vec2 { x: 4.0, y: 6.0 }
+ println!("{c:?}"); // 结果:Vec2 { x: 4.0, y: 6.0 }
}

-| Aspect | C++ | Rust | +| Aspect / 维度 | C++ | Rust | |––––|—–|——| -| Mechanism | Magic function names (operator+) | Implement a trait (impl Add for T) | +| Mechanism / 机制 | Magic function names / 魔法函数名 (operator+) | Implement a trait / 实现 Trait (impl Add for T) | -| Discovery | Grep for operator+ or read the header | Look at trait impls — IDE support excellent | +| Discovery / 发现方式 | Grep for operator+ or read the header | Look at trait impls / 查看 Trait 实现 —— IDE 支持极佳 | -| Return type | Free choice | Fixed by the Output associated type | +| Return type / 返回类型 | Free choice / 自由选择 | Fixed by the Output associated type / 由 Output 关联类型固定 | -| Receiver | Usually takes const T& (borrows) | Takes self by value (moves!) by default | +| Receiver / 接收端 | Usually takes const T& (borrows) | Takes self by value (moves! / 移动) by default | -| Symmetry | Can write impl operator+(int, Vec2) | Must add impl Add<Vec2> for i32 (foreign trait rules apply) | +| Symmetry / 对称性 | Can write impl operator+(int, Vec2) | Must add impl Add<Vec2> for i32 | -| << for printing | operator<<(ostream&, T) — overload for any stream | impl fmt::Display for T — one canonical to_string representation | +| << for printing / 用于打印的 << | operator<<(ostream&, T) | impl fmt::Display for T — 规范的字符串表示 |

  • In Rust, Add::add(self, rhs) takes self by value. For Copy types (like Vec2 above, which derives Copy) this is fine — the compiler copies. But for non-Copy types, + consumes the operands:
  • 在 Rust 中,Add::add(self, rhs)获取 self。对于 Copy 类型(例如上面的 Vec2,它派生了 Copy),这没问题 —— 编译器会进行拷贝。但对于非 Copy 类型,+消耗操作数:
#![allow(unused)]
fn main() {
let s1 = String::from("hello ");
let s2 = String::from("world");
- let s3 = s1 + &s2;  // s1 is MOVED into s3!
+ let s3 = s1 + &s2;  // s1 is MOVED into s3! / s1 移动到了 s3 中!
- // println!("{s1}");  // ❌ Compile error: value used after move
+ // println!("{s1}");  // ❌ Compile error / 编译错误:值在移动后被使用
- println!("{s2}");     // ✅ s2 was only borrowed (&s2)
+ println!("{s2}");     // ✅ s2 only borrowed / s2 只是被借用 (&s2)
}
  • This is why String + &str works but &str + &str does not — Add is only implemented for String + &str, consuming the left-hand String to reuse its buffer. This has no C++ analogue: std::string::operator+ always creates a new string.
  • 这就是为什么 String + &str 有效但 &str + &str 无效的原因 —— Add 仅为 String + &str 实现,消耗左侧的 String 以重用其缓冲区。这没有 C++ 的对应物:std::string::operator+ 总是创建一个新字符串。

-| C++ Operator | Rust Trait | Notes | +| C++ Operator / 运算符 | Rust Trait | Notes / 说明 | |———––|———–|—––| -| operator+ | std::ops::Add | Output associated type | +| operator+ | std::ops::Add | Output associated type / 关联类型 | -| operator- | std::ops::Sub | | -| operator- | std::ops::Sub | | -| operator* | std::ops::Mul | Not pointer deref — that’s Deref | +| operator* | std::ops::Mul | Not pointer deref / 不是指针解引用 —— 那是 Deref | -| operator/ | std::ops::Div | | -| operator/ | std::ops::Div | | -| operator% | std::ops::Rem | | -| operator/ | std::ops::Div | | -| operator% | std::ops::Rem | | -| operator- (unary) | std::ops::Neg | | -| operator- (unary) | std::ops::Neg | | -| operator! / operator~ | std::ops::Not | Rust uses ! for both logical and bitwise NOT (no ~ operator) | +| operator! / operator~ | std::ops::Not | Rust treats ! as both / Rust 将 ! 同时用于逻辑和位取反 | -| operator&, \|, ^ | BitAnd, BitOr, BitXor | | -| operator&, \|, ^ | BitAnd, BitOr, BitXor | | -| operator<<, >> (shift) | Shl, Shr | NOT stream I/O! | +| operator<<, >> (shift) | Shl, Shr | NOT stream I/O! / 不是流 I/O! | -| operator+= | std::ops::AddAssign | Takes &mut self (not self) | +| operator+= | std::ops::AddAssign | Takes &mut self / 使用 &mut self | -| operator[] | std::ops::Index / IndexMut | Returns &Output / &mut Output | +| operator[] | std::ops::Index / IndexMut | | -| operator() | Fn / FnMut / FnOnce | Closures implement these; you cannot impl Fn directly | +| operator() | Fn / FnMut / FnOnce | Closures implement these / 闭包实现了这些 | -| operator== | PartialEq (+ Eq) | In std::cmp, not std::ops | +| operator== | PartialEq (+ Eq) | In std::cmp | -| operator< | PartialOrd (+ Ord) | In std::cmp | +| operator< | PartialOrd (+ Ord) | In std::cmp | -| operator<< (stream) | fmt::Display | println!("{}", x) | -| operator<< (debug) | fmt::Debug | println!("{:?}", x) | -| operator bool | No direct equivalent | Use impl From<T> for bool or a named method like .is_empty() | +| operator bool | No equivalent / 无等价物 | Use From/Into or named method / 使用 From/Into 或命名方法 | -| operator T() (implicit conversion) | No implicit conversions | Use From/Into traits (explicit) | +| operator T() (implicit) | No implicit / 无隐式转换 | Use From/Into / 使用显式的 From/Into |

    1. No implicit conversions: C++ operator int() can cause silent, surprising casts. Rust has no implicit conversion operators — use From/Into and call .into() explicitly.
    1. No implicit conversions / 无隐式转换:C++ operator int() 可能会导致静默且令人惊讶的转换。Rust 没有隐式转换运算符 —— 请使用 From/Into 并显式调用 .into()
    1. No overloading && / ||: C++ allows it (breaking short-circuit semantics!). Rust does not.
    1. No overloading && / || / 不允许重载 && / ||:C++ 允许这样做(会破坏短路语义!)。Rust 不允许。
    1. No overloading =: Assignment is always a move or copy, never user-defined. Compound assignment (+=) IS overloadable via AddAssign, etc.
    1. No overloading = / 不允许重载 =:赋值始终是移动或拷贝,永远不能由用户定义。复合赋值(+=)可以通过 AddAssign 等进行重载。
    1. No overloading ,: C++ allows operator,() — one of the most infamous C++ footguns. Rust does not.
    1. No overloading , / 不允许重载 ,:C++ 允许 operator,() —— 这是 C++ 最臭名昭著的搬起石头砸自己脚的特性之一。Rust 不允许。
    1. No overloading & (address-of): Another C++ footgun (std::addressof exists to work around it). Rust’s & always means “borrow.”
    1. No overloading & (address-of) / 不允许重载 &(取地址):另一个 C++ 的坑点(std::addressof 的存在就是为了绕过它)。Rust 的 & 始终表示“借用”。
    1. Coherence rules: You can only implement Add<Foreign> for your own type, or Add<YourType> for a foreign type — never Add<Foreign> for Foreign. This prevents conflicting operator definitions across crates.
    1. Coherence rules / 一致性规则:你只能为自己的类型实现 Add<Foreign>,或为外部类型实现 Add<YourType> —— 永远不能为 Foreign 实现 Add<Foreign>。这可以防止各 crate 之间出现冲突的运算符定义。
  • Bottom line: In C++, operator overloading is powerful but largely unregulated — you can overload almost anything, including comma and address-of, and implicit conversions can trigger silently. Rust gives you the same expressiveness for arithmetic and comparison operators via traits, but blocks the historically dangerous overloads and forces all conversions to be explicit.

  • 底线:在 C++ 中,运算符重载功能强大但很大程度上不受监管 —— 你几乎可以重载任何东西,包括逗号和取地址符,而且隐式转换可能会静默触发。Rust 通过 trait 为你提供了相同的算术和比较运算符表达能力,但阻止了历史上危险的重载,并强制所有转换都是显式的。


    • Rust allows implementing a user defined trait on even built-in types like u32 in this example. However, either the trait or the type must belong to the crate
    • Rust 允许在甚至像本例中的 u32 这样的内置类型上实现用户定义的 trait。但是,trait 或类型中必须有一个属于当前的 crate。
trait IsSecret {
  fn is_secret(&self);
}
- // The IsSecret trait belongs to the crate, so we are OK
+ // The IsSecret trait belongs to the crate, so we are OK / IsSecret trait 属于本 crate,所以没问题
impl IsSecret for u32 {
  fn is_secret(&self) {
      if *self == 42 {
          println!("Is secret of life");
      }
  }
}

fn main() {
  42u32.is_secret();
  43u32.is_secret();
}
    • Traits support interface inheritance and default implementations
    • Trait 支持接口继承和默认实现
trait Animal {
-  // Default implementation
+  // Default implementation / 默认实现
  fn is_mammal(&self) -> bool {
    true
  }
}
trait Feline : Animal {
-  // Default implementation
+  // Default implementation / 默认实现
  fn is_feline(&self) -> bool {
    true
  }
}

struct Cat;
- // Use default implementations. Note that all traits for the supertrait must be individually implemented
+ // Use default implementations. Note that all traits for the supertrait must be individually implemented / 使用默认实现。注意父 trait 的所有 trait 都必须分别实现。
impl Feline for Cat {}
impl Animal for Cat {}
fn main() {
  let c = Cat{};
  println!("{} {}", c.is_mammal(), c.is_feline());
}

  • 🟡 Intermediate
  • 🟡 Intermediate / 中级
    • Implement a Log trait with a single method called log() that accepts a u64
    • 实现一个 Log trait,带有一个名为 log() 的方法,该方法接受一个 u64
  • - Implement two different loggers ```SimpleLogger``` and ```ComplexLogger``` that implement the ```Log trait```. One should output "Simple logger" with the ```u64``` and the other should output "Complex logger" with the ```u64``` 
    
  • - 实现两个不同的记录器 ```SimpleLogger``` 和 ```ComplexLogger```,它们实现 ```Log trait```。一个应输出 "Simple logger" 及其 ```u64``` 对应的值,另一个应输出 "Complex logger" 及其 ```u64``` 对应的值。
    
  • Solution (click to expand)
  • Solution (click to expand) / 解决方案(点击展开)
trait Log {
    fn log(&self, value: u64);
}

struct SimpleLogger;
struct ComplexLogger;

impl Log for SimpleLogger {
    fn log(&self, value: u64) {
        println!("Simple logger: {value}");
    }
}

impl Log for ComplexLogger {
    fn log(&self, value: u64) {
        println!("Complex logger: {value} (hex: 0x{value:x}, binary: {value:b})");
    }
}

fn main() {
    let simple = SimpleLogger;
    let complex = ComplexLogger;
    simple.log(42);
    complex.log(42);
}
- // Output:
+ // Output / 输出:
// Simple logger: 42
// Complex logger: 42 (hex: 0x2a, binary: 101010)

#[derive(Debug)]
struct Small(u32);
#[derive(Debug)]
struct Big(u32);
trait Double {
    type T;
    fn double(&self) -> Self::T;
}

impl Double for Small {
-    type T = Big;
+    type T = Big; // 指定关联类型
    fn double(&self) -> Self::T {
        Big(self.0 * 2)
    }
}
fn main() {
    let a = Small(42);
    println!("{:?}", a.double());
}
    • impl can be used with traits to accept any type that implements a trait
    • impl 关键字可以与 trait 一起使用,以接受实现该 trait 的任何类型
trait Pet {
    fn speak(&self);
}
struct Dog {}
struct Cat {}
impl Pet for Dog {
    fn speak(&self) {println!("Woof!")}
}
impl Pet for Cat {
    fn speak(&self) {println!("Meow")}
}
fn pet_speak(p: &impl Pet) {
    p.speak();
}
fn main() {
    let c = Cat {};
    let d = Dog {};
    pet_speak(&c);
    pet_speak(&d);
}
    • impl can be also be used be used in a return value
    • impl 关键字也可以用于返回值中
trait Pet {}
struct Dog;
struct Cat;
impl Pet for Cat {}
impl Pet for Dog {}
fn cat_as_pet() -> impl Pet {
    let c = Cat {};
    c
}
fn dog_as_pet() -> impl Pet {
    let d = Dog {};
    d
}
fn main() {
-    let p = cat_as_pet();
+    let _p = cat_as_pet();
-    let d = dog_as_pet();
+    let _d = dog_as_pet();
}

    • Dynamic traits can be used to invoke the trait functionality without knowing the underlying type. This is known as type erasure
    • 动态 trait 可用于在不知道底层类型的情况下调用 trait 功能。这被称为 类型擦除(type erasure)
trait Pet {
    fn speak(&self);
}
struct Dog {}
struct Cat {x: u32}
impl Pet for Dog {
    fn speak(&self) {println!("Woof!")}
}
impl Pet for Cat {
    fn speak(&self) {println!("Meow")}
}
- fn pet_speak(p: &dyn Pet) {
+ fn pet_speak(p: &dyn Pet) { // 动态分派
    p.speak();
}
fn main() {
    let c = Cat {x: 42};
    let d = Dog {};
    pet_speak(&c);
    pet_speak(&d);
}

  • These three approaches all achieve polymorphism but with different trade-offs:
  • 这三种方法都能实现多态,但具有不同的权衡:

-| Approach | Dispatch | Performance | Heterogeneous collections? | When to use | +| Approach / 方法 | Dispatch / 分派 | Performance / 性能 | Heterogeneous / 异构集合? | When to use / 何时使用 | |–––––|–––––|———––|—————————|———––| -| impl Trait / generics | Static (monomorphized) | Zero-cost — inlined at compile time | No — each slot has one concrete type | Default choice. Function arguments, return types | -| dyn Trait | Dynamic (vtable) | Small overhead per call (~1 pointer indirection) | Yes — Vec<Box<dyn Trait>> | When you need mixed types in a collection, or plugin-style extensibility | -| enum | Match | Zero-cost — known variants at compile time | Yes — but only known variants | When the set of variants is closed and known at compile time | +| impl Trait / 泛型 | Static / 静态 (monomorphized) | Zero-cost / 零成本 —— 编译时内联 | No / 否 —— 每个位置只能有一种具体类型 | 默认选择。函数参数、返回类型 | +| dyn Trait | Dynamic / 动态 (vtable) | Small overhead / 微小开销 | Yes / 是 —— Vec<Box<dyn Trait>> | 需要混合类型或插件式扩展时 | +| enum | Match | Zero-cost / 零成本 | Yes / 是 —— 但仅限于已知的变体 | 变体集合是封闭的且在编译时已知时 |

#![allow(unused)]
fn main() {
trait Shape {
    fn area(&self) -> f64;
}
struct Circle { radius: f64 }
struct Rect { w: f64, h: f64 }
impl Shape for Circle { fn area(&self) -> f64 { std::f64::consts::PI * self.radius * self.radius } }
impl Shape for Rect   { fn area(&self) -> f64 { self.w * self.h } }

- // Static dispatch — compiler generates separate code for each type
+ // Static dispatch — compiler generates separate code for each type / 静态分派 —— 编译器为每种类型生成独立代码
fn print_area(s: &impl Shape) { println!("{}", s.area()); }

- // Dynamic dispatch — one function, works with any Shape behind a pointer
+ // Dynamic dispatch — one function, works with any Shape behind a pointer / 动态分派 —— 一个函数即可处理指针后的任何 Shape
fn print_area_dyn(s: &dyn Shape) { println!("{}", s.area()); }

- // Enum — closed set, no trait needed
+ // Enum — closed set, no trait needed / 枚举 —— 封闭集合,不需要 trait
enum ShapeEnum { Circle(f64), Rect(f64, f64) }
impl ShapeEnum {
    fn area(&self) -> f64 {
        match self {
            ShapeEnum::Circle(r) => std::f64::consts::PI * r * r,
            ShapeEnum::Rect(w, h) => w * h,
        }
    }
}
}
  • For C++ developers: impl Trait is like C++ templates (monomorphized, zero-cost). dyn Trait is like C++ virtual functions (vtable dispatch). Rust enums with match are like std::variant with std::visit — but exhaustive matching is enforced by the compiler.

  • For C++ developers / C++ 开发者注意: impl Trait 类似于 C++ 模板(单态化、零成本)。dyn Trait 类似于 C++ 虚函数(虚表分派)。带有 match 的 Rust 枚举类似于 std::variantstd::visit 的结合 —— 但 Rust 编译器会强制执行穷尽匹配。

  • Rule of thumb: Start with impl Trait (static dispatch). Reach for dyn Trait only when you need heterogeneous collections or can’t know the concrete type at compile time. Use enum when you own all the variants.

  • 经验法则:从 impl Trait(静态分派)开始。仅当你需要异构集合或在编译时无法确定具体类型时,才求助于 dyn Trait。当你拥有所有变体时,使用 enum

Rust generics / Rust 泛型

What you’ll learn / 你将学到: Generic type parameters, monomorphization (zero-cost generics), trait bounds, and how Rust generics compare to C++ templates — with better error messages and no SFINAE.

泛型类型参数、单态化(零成本泛型)、trait 限定,以及 Rust 泛型与 C++ 模板的点对点对比 —— 拥有更好的错误消息且无需 SFINAE。

  • Generics allow the same algorithm or data structure to be reused across data types
    • 泛型允许相同的算法或数据结构在不同的数据类型之间复用
  • - The generic parameter appears as an identifier within ```<>```, e.g.: ```<T>```. The parameter can have any legal identifier name, but is typically kept short for brevity
    
  • - 泛型参数以 ```<>``` 内的标识符形式出现,例如:```<T>```。该参数可以是任何合法的标识符名称,但通常为了简洁而保持简短。
    
  • - The compiler performs monomorphization at compile time, i.e., it generates a new type for every variation of ```T``` that is encountered
    
  • - 编译器在编译时执行**单态化(monomorphization)**,即为遇到的每种 ```T``` 的变体生成一个新的类型版本。
    
-// Returns a tuple of type <T> composed of left and right of type <T>
+// Returns a tuple of type <T> composed of left and right of type <T> / 返回一个由类型为 <T> 的 left 和 right 组成的元组
fn pick<T>(x: u32, left: T, right: T) -> (T, T) {
   if x == 42 {
    (left, right) 
   } else {
    (right, left)
   }
}
fn main() {
    let a = pick(42, true, false);
    let b = pick(42, "hello", "world");
    println!("{a:?}, {b:?}");
}
    • Generics can also be applied to data types and associated methods. It is possible to specialize the implementation for a specific <T> (example: f32 vs. u32)
    • 泛型也可以应用于数据类型和关联方法。可以针对特定的 <T> 进行专有化实现(例如:f32u32 的区别)。
- #[derive(Debug)] // We will discuss this later
+ #[derive(Debug)] // We will discuss this later / 我们稍后会讨论这个
struct Point<T> {
    x : T,
    y : T,
}
impl<T> Point<T> {
    fn new(x: T, y: T) -> Self {
        Point {x, y}
    }
    fn set_x(&mut self, x: T) {
         self.x = x;       
    }
    fn set_y(&mut self, y: T) {
         self.y = y;       
    }
}
- impl Point<f32> {
+ impl Point<f32> { // 为 f32 类型专门实现的方法
    fn is_secret(&self) -> bool {
        self.x == 42.0
    }    
}
fn main() {
-    let mut p = Point::new(2, 4); // i32
+    let mut p = Point::new(2, 4); // i32 类型
-    let q = Point::new(2.0, 4.0); // f32 类型
+    let q = Point::new(2.0, 4.0); // f32 类型
    p.set_x(42);
    p.set_y(43);
    println!("{p:?} {q:?} {}", q.is_secret());
}
  • 🟢 Starter
  • 🟢 Starter / 入门级
    • Modify the Point type to use two different types (T and U) for x and y
    • 修改 Point 类型,使其在 x 和 y 上使用两种不同的类型(TU)。
  • Solution (click to expand)
  • Solution (click to expand) / 解决方案(点击展开)
#[derive(Debug)]
struct Point<T, U> {
    x: T,
    y: U,
}

impl<T, U> Point<T, U> {
    fn new(x: T, y: U) -> Self {
        Point { x, y }
    }
}

fn main() {
-    let p1 = Point::new(42, 3.14);        // Point<i32, f64>
+    let p1 = Point::new(42, 3.14);        // 类型为 Point<i32, f64>
-    let p2 = Point::new("hello", true);   // Point<&str, bool>
+    let p2 = Point::new("hello", true);   // 类型为 Point<&str, bool>
-    let p3 = Point::new(1u8, 1000u64);    // Point<u8, u64>
+    let p3 = Point::new(1u8, 1000u64);    // 类型为 Point<u8, u64>
    println!("{p1:?}");
    println!("{p2:?}");
    println!("{p3:?}");
}
- // Output:
+ // Output / 输出:
// Point { x: 42, y: 3.14 }
// Point { x: "hello", y: true }
// Point { x: 1, y: 1000 }
    • Traits can be used to place restrictions on generic types (constraints)
    • Trait 可用于对泛型类型施加限制(约束/限定)
    • The constraint can be specified using a : after the generic type parameter, or using where. The following defines a generic function get_area that takes any type T as long as it implements the ComputeArea trait
    • 约束可以使用泛型类型参数后的 : 来指定,也可以使用 where 子句。下面定义了一个泛型函数 get_area,它接受任何实现了 ComputeArea trait 的类型 T
#![allow(unused)]
fn main() {
    trait ComputeArea {
        fn area(&self) -> u64;
    }
-    fn get_area<T: ComputeArea>(t: &T) -> u64 {
+    fn get_area<T: ComputeArea>(t: &T) -> u64 { // 使用冒号指定约束
        t.area()
    }
}
    • It is possible to have multiple trait constraints
    • 可以有多个 trait 约束
trait Fish {}
trait Mammal {}
struct Shark;
struct Whale;
impl Fish for Shark {}
impl Fish for Whale {}
impl Mammal for Whale {}
- fn only_fish_and_mammals<T: Fish + Mammal>(_t: &T) {}
+ fn only_fish_and_mammals<T: Fish + Mammal>(_t: &T) {} // 必须同时实现 Fish 和 Mammal
fn main() {
    let w = Whale {};
-    only_fish_and_mammals(&w);
+    only_fish_and_mammals(&w); // 成功
    let _s = Shark {};
-    // Won't compile
+    // Won't compile / 无法编译
    only_fish_and_mammals(&_s);
}
    • Trait constraints can be combined with generics in data types
    • Trait 约束可以与数据类型中的泛型相结合。
    • In the following example, we define the PrintDescription trait and a generic struct Shape with a member constrained by the trait
    • 在下面的示例中,我们定义了 PrintDescription trait 和一个带有受该 trait 约束的成员的泛型 struct Shape
#![allow(unused)]
fn main() {
trait PrintDescription {
    fn print_description(&self);
}
struct Shape<S: PrintDescription> {
    shape: S,
}
- // Generic Shape implementation for any type that implements PrintDescription
+ // Generic Shape implementation / 针对任何实现 PrintDescription 的类型的泛型 Shape 实现
impl<S: PrintDescription> Shape<S> {
    fn print(&self) {
        self.shape.print_description();
    }
}
}
  • 🟡 Intermediate
  • 🟡 Intermediate / 中级
    • Implement a struct with a generic member cipher that implements CipherText
    • 实现一个带有一个实现了 CipherText 的泛型成员 cipherstruct
#![allow(unused)]
fn main() {
trait CipherText {
    fn encrypt(&self);
}
- // TO DO
+ // TO DO / 待办
//struct Cipher<>

}
    • Next, implement a method called encrypt on the struct impl that invokes encrypt on cipher
    • 接着,在 structimpl 中实现一个名为 encrypt 的方法,该方法在 cipher 上调用 encrypt
#![allow(unused)]
fn main() {
- // TO DO
+ // TO DO / 待办
impl for Cipher<> {}
}
    • Next, implement CipherText on two structs called CipherOne and CipherTwo (just println() is fine). Create CipherOne and CipherTwo, and use Cipher to invoke them
    • 接着,在名为 CipherOneCipherTwo 的两个结构体上实现 CipherText(仅使用 println() 即可)。创建 CipherOneCipherTwo 实例,并使用 Cipher 来调用它们。
  • Solution (click to expand)
  • Solution (click to expand) / 解决方案(点击展开)
trait CipherText {
    fn encrypt(&self);
}

struct Cipher<T: CipherText> {
    cipher: T,
}

impl<T: CipherText> Cipher<T> {
    fn encrypt(&self) {
        self.cipher.encrypt();
    }
}

struct CipherOne;
struct CipherTwo;

impl CipherText for CipherOne {
    fn encrypt(&self) {
        println!("CipherOne encryption applied");
    }
}

impl CipherText for CipherTwo {
    fn encrypt(&self) {
        println!("CipherTwo encryption applied");
    }
}

fn main() {
    let c1 = Cipher { cipher: CipherOne };
    let c2 = Cipher { cipher: CipherTwo };
    c1.encrypt();
    c2.encrypt();
}
- // Output:
+ // Output / 输出:
// CipherOne encryption applied
// CipherTwo encryption applied
    • Rust types can be used to enforce state machine transitions at compile time
    • Rust 类型可用于在编译时强制执行状态机转换。
  • - Consider a ```Drone``` with say two states: ```Idle``` and ```Flying```. In the ```Idle``` state, the only permitted method is ```takeoff()```. In the ```Flying``` state, we permit ```land()```
    
  • - 考虑一个具有两种状态(比如 ```Idle``` 和 ```Flying```)的 ```Drone```(无人机)。在 ```Idle``` 状态下,唯一允许的方法是 ```takeoff()```(起飞)。在 ```Flying``` 状态下,我们允许 ```land()```(降落)。
    
    • One approach is to model the state machine using something like the following
    • 一种方法是使用类似以下的方式为状态机建模:
#![allow(unused)]
fn main() {
enum DroneState {
    Idle,
    Flying
}
- struct Drone {x: u64, y: u64, z: u64, state: DroneState}  // x, y, z are coordinates
+ struct Drone {x: u64, y: u64, z: u64, state: DroneState}  // x, y, z 为坐标
}
    • This requires a lot of runtime checks to enforce the state machine semantics — ▶ try it to see why
    • 这需要大量的运行时检查来强制执行状态机语义 —— ▶ 尝试一下 看看为什么。
    • Generics allows us to enforce the state machine at compile time. This requires using a special generic called PhantomData<T>
    • 泛型允许我们在编译时强制执行状态机。这需要使用一个名为 PhantomData<T> 的特殊泛型。
    • The PhantomData<T> is a zero-sized marker data type. In this case, we use it to represent the Idle and Flying states, but it has zero runtime size
    • PhantomData<T> 是一个零大小(zero-sized)的标记数据类型。在本例中,我们使用它来表示 IdleFlying 状态,但它的运行时大小为
    • Notice that the takeoff and land methods take self as a parameter. This is referred to as consuming (contrast with &self which uses borrowing). Basically, once we call the takeoff() on Drone<Idle>, we can only get back a Drone<Flying> and viceversa
    • 请注意,takeoffland 方法将 self 作为参数。这被称为消耗(consuming)(与使用借用的 &self 相对)。基本上,一旦我们在 Drone<Idle> 上调用了 takeoff(),我们只能得到一个 Drone<Flying>,反之亦然。
#![allow(unused)]
fn main() {
struct Drone<T> {x: u64, y: u64, z: u64, state: PhantomData<T> }
impl Drone<Idle> {
    fn takeoff(self) -> Drone<Flying> {...}
}
impl Drone<Flying> {
    fn land(self) -> Drone<Idle> { ...}
}
}
  • - [▶ Try it in the Rust Playground](https://play.rust-lang.org/)
    
  • - [▶ 在 Rust Playground 中尝试](https://play.rust-lang.org/)
    
    • Key takeaways:
  • - States can be represented using structs (zero-size)
    
    • 状态可以使用结构体表示(零大小)。
  • - We can combine the state ```T``` with ```PhantomData<T>``` (zero-size)
    
    • 我们可以将状态 TPhantomData<T> 结合(零大小)。
  • - Implementing the methods for a particular stage of the state machine is now just a matter of ```impl State<T>```
    
    • 为状态机的特定阶段实现方法现在只需 impl State<T>
  • - Use a method that consumes ```self``` to transition from one state to another
    
    • 使用消耗 self 的方法实现从一个状态到另一个状态的转换。
  • - This gives us ```zero cost``` abstractions. The compiler can enforce the state machine at compile time and it's impossible to call methods unless the state is right
    
    • 这为我们提供了**零成本(zero cost)**抽象。编译器可以在编译时强制执行状态机,并且除非状态正确,否则不可能调用某些方法。
    • The consume self can be useful for builder patterns
    • 消耗 self 在构建器模式中非常有用。
    • Consider a GPIO configuration with several dozen pins. The pins can be configured to high or low (default is low)
    • 考虑一个具有几十个引脚的 GPIO 配置。引脚可以配置为高电平或低电平(默认为低)。
#![allow(unused)]
fn main() {
#[derive(default)]
enum PinState {
    #[default]
    Low,
    High,
} 
#[derive(default)]
struct GPIOConfig {
    pin0: PinState,
    pin1: PinState
-    ... 
+    // ... 
}
}
    • The builder pattern can be used to construct a GPIO configuration by chaining — ▶ Try it
    • 构建器模式可以通过链式调用来构造 GPIO 配置 —— ▶ 尝试一下

Rust From and Into traits / Rust From 与 Into Trait

What you’ll learn / 你将学到: Rust’s type conversion traits — From<T> and Into<T> for infallible conversions, TryFrom and TryInto for fallible ones. Implement From and get Into for free. Replaces C++ conversion operators and constructors.

Rust 的类型转换 trait —— 用于无误转换的 From<T>Into<T>,以及用于可能失败转换的 TryFromTryInto。实现 From 即可免费获得 Into。取代了 C++ 的转换运算符和构造函数。

  • From and Into are complementary traits to facilitate type conversion
    • FromInto 是互补的 trait,旨在简化类型转换。
    • Types normally implement on the From trait. the String::from() converts from “&str” to String, and compiler can automatically derive &str.into
    • 类型通常实现 From trait。比如 String::from() 将 “&str” 转换为 String,编译器可以自动根据此推导出 &str.into 的实现。
struct Point {x: u32, y: u32}
-// Construct a Point from a tuple
+// Construct a Point from a tuple / 从元组构造 Point
impl From<(u32, u32)> for Point {
    fn from(xy : (u32, u32)) -> Self {
-        Point {x : xy.0, y: xy.1}       // Construct Point using the tuple elements
+        Point {x : xy.0, y: xy.1}       // Using elements / 使用元组元素构造 Point
    }
}
fn main() {
    let s = String::from("Rust");
    let x = u32::from(true);
    let p = Point::from((40, 42));
-    // let p : Point = (40.42)::into(); // Alternate form of the above
+    // let p : Point = (40.42)::into(); // Alternate form / 上述形式的另一种写法
    println!("s: {s} x:{x} p.x:{} p.y {}", p.x, p.y);   
}
    • Implement a From trait for Point to convert into a type called TransposePoint. TransposePoint swaps the x and y elements of Point
    • Point 实现 From trait,将其转换为名为 TransposePoint 的类型。TransposePoint 会交换 Pointxy 元素。
  • Solution (click to expand)
  • Solution (click to expand) / 解决方案(点击展开)
struct Point { x: u32, y: u32 }
struct TransposePoint { x: u32, y: u32 }

impl From<Point> for TransposePoint {
    fn from(p: Point) -> Self {
        TransposePoint { x: p.y, y: p.x }
    }
}

fn main() {
    let p = Point { x: 10, y: 20 };
    let tp = TransposePoint::from(p);
-    println!("Transposed: x={}, y={}", tp.x, tp.y);  // x=20, y=10
+    println!("Transposed: x={}, y={}", tp.x, tp.y);  // 结果:x=20, y=10

-    // Using .into() — works automatically when From is implemented
+    // Using .into() — works automatically / 使用 .into() —— 在实现 From 后自动起效
    let p2 = Point { x: 3, y: 7 };
    let tp2: TransposePoint = p2.into();
-    println!("Transposed: x={}, y={}", tp2.x, tp2.y);  // x=7, y=3
+    println!("Transposed: x={}, y={}", tp2.x, tp2.y);  // 结果:x=7, y=3
}
- // Output:
+ // Output / 输出:
// Transposed: x=20, y=10
// Transposed: x=7, y=3
    • Default can be used to implement default values for a type
    • Default 可用于为类型实现默认值。
  • - Types can use the ```Derive``` macro with ```Default``` or provide a custom implementation
    
  • - 类型可以使用 ```Derive``` 宏自动派生 ```Default```,也可以提供自定义实现。
    
#[derive(Default, Debug)]
struct Point {x: u32, y: u32}
#[derive(Debug)]
struct CustomPoint {x: u32, y: u32}
impl Default for CustomPoint {
    fn default() -> Self {
        CustomPoint {x: 42, y: 42}
    }
}
fn main() {
-    let x = Point::default();   // Creates a Point{0, 0}
+    let x = Point::default();   // Creates / 创建一个 Point{0, 0}
    println!("{x:?}");
    let y = CustomPoint::default();
    println!("{y:?}");
}
    • Default trait has several use cases including
    • Default trait 具有多种用例,包括:
  • - Performing a partial copy and using default initialization for rest
    
  • - 执行部分复制,并对其余部分使用默认初始化。
    
  • - Default alternative for ```Option``` types in methods like ```unwrap_or_default()```
    
  • - 作为 ```Option``` 类型的默认备选项,例如在 ```unwrap_or_default()``` 方法中。
    
#[derive(Debug)]
struct CustomPoint {x: u32, y: u32}
impl Default for CustomPoint {
    fn default() -> Self {
        CustomPoint {x: 42, y: 42}
    }
}
fn main() {
    let x = CustomPoint::default();
-    // Override y, but leave rest of elements as the default
+    // Override y, but leave rest default / 覆盖 y,但其余元素保留为默认值
    let y = CustomPoint {y: 43, ..CustomPoint::default()};
    println!("{x:?} {y:?}");
    let z : Option<CustomPoint> = None;
-    // Try changing the unwrap_or_default() to unwrap()
+    // Try changing unwrap_or_default() to unwrap() / 尝试将 unwrap_or_default() 更改为 unwrap()
    println!("{:?}", z.unwrap_or_default());
}
    • Rust doesn’t support implicit type conversions and as can be used for explicit conversions
    • Rust 不支持隐式类型转换,可以使用 as 进行显式转换。
    • as should be sparingly used because it’s subject to loss of data by narrowing and so forth. In general, it’s preferable to use into() or from() where possible
    • 应当谨慎使用 as,因为它可能会导致由于范围收窄(narrowing)等原因产生的数据丢失。通常情况下,尽可能使用 into()from() 更好。
fn main() {
    let f = 42u8;
-    // let g : u32 = f;    // Will not compile
+    // let g : u32 = f;    // Will not compile / 无法编译
-    let g = f as u32;      // Ok, but not preferred. Subject to rules around narrowing
+    let g = f as u32;      // Ok, but not preferred / 可以使用但不推荐。受收窄规则限制
-    let g : u32 = f.into(); // Most preferred form; infallible and checked by the compiler
+    let g : u32 = f.into(); // Most preferred / 最推荐的形式;无误且由编译器检查
-    //let k : u8 = f.into();  // Fails to compile; narrowing can result in loss of data
+    //let k : u8 = f.into();  // Fails to compile / 编译失败;收窄可能导致数据丢失
    
-    // Attempting a narrowing operation requires use of try_into
+    // Narrowing requires try_into / 尝试收窄操作需要使用 try_into
    if let Ok(k) = TryInto::<u8>::try_into(g) {
        println!("{k}");
    }
}

12. Closures / 12. 闭包

Rust closures / Rust 闭包

What you’ll learn / 你将学到: Closures as anonymous functions, the three capture traits (Fn, FnMut, FnOnce), move closures, and how Rust closures compare to C++ lambdas — with automatic capture analysis instead of manual [&]/[=] specifications.

闭包作为匿名函数、三种捕获 trait(FnFnMutFnOnce)、move 闭包,以及 Rust 闭包与 C++ lambda 的对比 —— Rust 具有自动捕获分析,而非手动的 [&] / [=] 规范。

  • Closures are anonymous functions that can capture their environment
    • 闭包是可以捕获其环境的匿名函数。
  • - C++ equivalent: lambdas (`[&](int x) { return x + 1; }`)
    
  • - C++ 等价物:lambda 表达式(`[&](int x) { return x + 1; }`)
    
  • - Key difference: Rust closures have **three** capture traits (`Fn`, `FnMut`, `FnOnce`) that the compiler selects automatically
    
  • - 关键区别:Rust 闭包有**三种**捕获 trait(`Fn`、`FnMut`、`FnOnce`),编译器会自动选择。
    
  • - C++ capture modes (`[=]`, `[&]`, `[this]`) are manual and error-prone (dangling `[&]`!)
    
  • - C++ 捕获模式(`[=]`、`[&]`、`[this]`)是手动的且容易出错(容易产生悬挂引用的 `[&]`!)。
    
  • - Rust's borrow checker prevents dangling captures at compile time
    
  • - Rust 的借用检查器在编译时防止悬挂捕获。
    
    • Closures can be identified by the || symbol. The parameters for the types are enclosed within the || and can use type inference
    • 闭包通过 || 符号来识别。参数类型包含在 || 之内,并且可以使用类型推断。
    • Closures are frequently used in conjunction with iterators (next topic)
    • 闭包经常与迭代器(下一个主题)结合使用。
fn add_one(x: u32) -> u32 {
    x + 1
}
fn main() {
-    let add_one_v1 = |x : u32| {x + 1}; // Explicitly specified type
+    let add_one_v1 = |x : u32| {x + 1}; // Explicitly specified / 显式指定类型
-    let add_one_v2 = |x| {x + 1};   // Type is inferred from call site
+    let add_one_v2 = |x| {x + 1};   // Inferred / 类型由调用处推断
-    let add_one_v3 = |x| x+1;   // Permitted for single line functions
+    let add_one_v3 = |x| x+1;   // Allowed for single line / 允许用于单行函数
    println!("{} {} {} {}", add_one(42), add_one_v1(42), add_one_v2(42), add_one_v3(42) );
}
  • 🟡 Intermediate
  • 🟡 Intermediate / 中级
    • Create a closure that captures a String from the enclosing scope and appends to it (hint: use move)
    • 创建一个从外层作用域捕获 String 并向其追加内容的闭包(提示:使用 move)。
    • Create a vector of closures: Vec<Box<dyn Fn(i32) -> i32>> containing closures that add 1, multiply by 2, and square the input. Iterate over the vector and apply each closure to the number 5
    • 创建一个闭包向量:Vec<Box<dyn Fn(i32) -> i32>>,其中包含加 1、乘 2 和平方输入的闭包。遍历向量并将每个闭包应用于数字 5。
  • Solution (click to expand)
  • Solution (click to expand) / 解决方案(点击展开)
fn main() {
-    // Part 1: Closure that captures and appends to a String
+    // Part 1: Capture and append / 第 1 部分:捕获并追加到 String
    let mut greeting = String::from("Hello");
    let mut append = |suffix: &str| {
        greeting.push_str(suffix);
    };
    append(", world");
    append("!");
    println!("{greeting}");  // "Hello, world!"

-    // Part 2: Vector of closures
+    // Part 2: Vector of closures / 第 2 部分:闭包向量
    let operations: Vec<Box<dyn Fn(i32) -> i32>> = vec![
-        Box::new(|x| x + 1),      // add 1
+        Box::new(|x| x + 1),      // add 1 / 加 1
-        Box::new(|x| x * 2),      // multiply by 2
+        Box::new(|x| x * 2),      // multiply by 2 / 乘 2
-        Box::new(|x| x * x),      // square
+        Box::new(|x| x * x),      // square / 平方
    ];

    let input = 5;
    for (i, op) in operations.iter().enumerate() {
        println!("Operation {i} on {input}: {}", op(input));
    }
}
- // Output:
+ // Output / 输出:
// Hello, world!
// Operation 0 on 5: 6
// Operation 1 on 5: 10
// Operation 2 on 5: 25
    • Iterators are one of the most powerful features of Rust. They enable very elegant methods for perform operations on collections, including filtering (filter()), transformation (map()), filter and map (filter_and_map()), searching (find()) and much more
    • 迭代器是 Rust 最强大的特性之一。它们为在集合上执行操作提供了非常优雅的方法,包括过滤(filter())、转换(map())、过滤并转换(filter_and_map())、搜索(find())等等。
    • In the example below, the |&x| *x >= 42 is a closure that performs the same comparison. The |x| println!("{x}") is another closure
    • 在下面的示例中,|&x| *x >= 42 是一个执行相同比较的闭包。|x| println!("{x}") 是另一个闭包。
fn main() {
    let a = [0, 1, 2, 3, 42, 43];
    for x in &a {
        if *x >= 42 {
            println!("{x}");
        }
    }
-    // Same as above
+    // Same as above / 与上面相同
    a.iter().filter(|&x| *x >= 42).for_each(|x| println!("{x}"))
}
    • A key feature of iterators is that most of them are lazy, i.e., they do not do anything until they are evaluated. For example, a.iter().filter(|&x| *x >= 42); wouldn’t have done anything without the for_each. The Rust compiler emits an explicit warning when it detects such a situation
    • 迭代器的一个关键特性是大多数迭代器都是**惰性(lazy)**的,即在被求值之前它们不会执行任何操作。例如,如果没有 for_eacha.iter().filter(|&x| *x >= 42); 什么也不会做。当 Rust 编译器检测到这种情况时,会发出显式的警告。
fn main() {
    let a = [0, 1, 2, 3, 42, 43];
-    // Add one to each element and print it
+    // Add one and print / 对每个元素加 1 并打印
    let _ = a.iter().map(|x|x + 1).for_each(|x|println!("{x}"));
    let found = a.iter().find(|&x|*x == 42);
    println!("{found:?}");
-    // Count elements
+    // Count elements / 统计元素数量
    let count = a.iter().count();
    println!("{count}");
}
    • The collect() method can be used to gather the results into a separate collection
    • collect() 方法可用于将结果收集到一个单独的集合中。
  • - In the below the ```_``` in ```Vec<_>``` is the equivalent of a wildcard character for the type returned by the ```map```. For example, we can even return a ```String``` from ```map``` 
    
  • - 在下面,```Vec<_>``` 中的 ```_``` 相当于 ```map``` 返回类型的通配符。例如,我们甚至可以从 ```map``` 中返回一个 ```String```。
    
fn main() {
    let a = [0, 1, 2, 3, 42, 43];
    let squared_a : Vec<_> = a.iter().map(|x|x*x).collect();
    for x in &squared_a {
        println!("{x}");
    }
-    let squared_a_strings : Vec<_> = a.iter().map(|x|(x*x).to_string()).collect();
+    let squared_a_strings : Vec<_> = a.iter().map(|x|(x*x).to_string()).collect(); // 收集为字符串向量
-    // These are actually string representations
+    // These are actually string representations / 这些实际上是字符串表示
    for x in &squared_a_strings {
        println!("{x}");
    }
}
  • 🟢 Starter
  • 🟢 Starter / 入门级
    • Create an integer array composed of odd and even elements. Iterate over the array and split it into two different vectors with even and odd elements in each
    • 创建一个包含奇数和偶数元素的整数数组。遍历该数组并将其拆分为两个分别包含偶数和奇数元素的向量。
    • Can this be done in a single pass (hint: use partition())?
    • 这能在一次遍历中完成吗(提示:使用 partition())?
  • Solution (click to expand)
  • Solution (click to expand) / 解决方案(点击展开)
fn main() {
    let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

-    // Approach 1: Manual iteration
+    // Approach 1: Manual iteration / 方法 1:手动迭代
    let mut evens = Vec::new();
    let mut odds = Vec::new();
    for n in numbers {
        if n % 2 == 0 {
            evens.push(n);
        } else {
            odds.push(n);
        }
    }
    println!("Evens: {evens:?}");
    println!("Odds:  {odds:?}");

-    // Approach 2: Single pass with partition()
+    // Approach 2: Single pass with partition() / 方法 2:使用 partition() 一次完成
    let (evens, odds): (Vec<i32>, Vec<i32>) = numbers
        .into_iter()
        .partition(|n| n % 2 == 0);
    println!("Evens (partition): {evens:?}");
    println!("Odds  (partition): {odds:?}");
}
- // Output:
+ // Output / 输出:
// Evens: [2, 4, 6, 8, 10]
// Odds:  [1, 3, 5, 7, 9]
// Evens (partition): [2, 4, 6, 8, 10]
// Odds  (partition): [1, 3, 5, 7, 9]
  • The following iterator adapters are used extensively in production Rust code. C++ has
  • 以下迭代器适配器在生产级 Rust 代码中被广泛使用。虽然 C++ 有
  • <algorithm> and C++20 ranges, but Rust’s iterator chains are more composable
  • <algorithm> 和 C++20 ranges,但 Rust 的迭代器链更具组合性
  • and more commonly used.
  • 且使用更普遍。
#![allow(unused)]
fn main() {
let sensors = vec!["temp0", "temp1", "temp2"];
for (idx, name) in sensors.iter().enumerate() {
    println!("Sensor {idx}: {name}");
}
// Sensor 0: temp0
// Sensor 1: temp1
// Sensor 2: temp2
}
  • C++ equivalent: for (size_t i = 0; i < sensors.size(); ++i) { auto& name = sensors[i]; ... }
  • C++ 等价物:for (size_t i = 0; i < sensors.size(); ++i) { auto& name = sensors[i]; ... }
#![allow(unused)]
fn main() {
let names = ["gpu0", "gpu1", "gpu2"];
let temps = [72.5, 68.0, 75.3];

let report: Vec<String> = names.iter()
    .zip(temps.iter())
    .map(|(name, temp)| format!("{name}: {temp}°C"))
    .collect();
println!("{report:?}");
// ["gpu0: 72.5°C", "gpu1: 68.0°C", "gpu2: 75.3°C"]

- // Stops at the shorter iterator — no out-of-bounds risk
+ // Stops at the shorter iterator / 在较短的迭代器处停止 —— 无越界风险
}
  • C++ equivalent: for (size_t i = 0; i < std::min(names.size(), temps.size()); ++i) { ... }
  • C++ 等价物:for (size_t i = 0; i < std::min(names.size(), temps.size()); ++i) { ... }
#![allow(unused)]
fn main() {
- // Each GPU has multiple PCIe BDFs; collect all BDFs across all GPUs
+ // Each GPU has multiple PCIe BDFs / 每个 GPU 有多个 PCIe BDF;收集所有 GPU 的所有 BDF
let gpu_bdfs = vec![
    vec!["0000:01:00.0", "0000:02:00.0"],
    vec!["0000:41:00.0"],
    vec!["0000:81:00.0", "0000:82:00.0"],
];

let all_bdfs: Vec<&str> = gpu_bdfs.iter()
    .flat_map(|bdfs| bdfs.iter().copied())
    .collect();
println!("{all_bdfs:?}");
// ["0000:01:00.0", "0000:02:00.0", "0000:41:00.0", "0000:81:00.0", "0000:82:00.0"]
}
  • C++ equivalent: nested for loop pushing into a single vector.
  • C++ 等价物:嵌套 for 循环并将结果推入单个 vector。
#![allow(unused)]
fn main() {
let critical_gpus = vec!["gpu0", "gpu3"];
let warning_gpus = vec!["gpu1", "gpu5"];

- // Process all flagged GPUs, critical first
+ // Process all flagged GPUs / 处理所有标记的 GPU,优先处理 critical(危急)
for gpu in critical_gpus.iter().chain(warning_gpus.iter()) {
    println!("Flagged: {gpu}");
}
}
#![allow(unused)]
fn main() {
let temps = [70, 72, 75, 73, 71, 68, 65];

- // windows(3): sliding window of size 3 — detect trends
+ // windows(3): sliding window of size 3 / 窗口大小为 3 的滑动窗口 —— 检测趋势
let rising = temps.windows(3)
    .any(|w| w[0] < w[1] && w[1] < w[2]);
- println!("Rising trend detected: {rising}"); // true (70 < 72 < 75)
+ println!("Rising trend detected: {rising}"); // true / 是 (70 < 72 < 75)

- // chunks(2): fixed-size groups — process in pairs
+ // chunks(2): fixed-size groups / 固定大小的分组 —— 成对处理
for pair in temps.chunks(2) {
    println!("Pair: {pair:?}");
}
// Pair: [70, 72]
// Pair: [75, 73]
// Pair: [71, 68]
- // Pair: [65]       ← last chunk can be smaller
+ // Pair: [65]       ← last chunk can be smaller / 最后一个分块可以更小
}
  • C++ equivalent: manual index arithmetic with i and i+1/i+2.
  • C++ 等价物:手动的索引算术,如 ii+1 / i+2
#![allow(unused)]
fn main() {
let errors = vec![
    ("gpu0", 3u32),
    ("gpu1", 0),
    ("gpu2", 7),
    ("gpu3", 1),
];

- // Count total errors and build summary in one pass
+ // Count total errors and summary / 一次完成总错误统计及详情摘要构建
let (total, summary) = errors.iter().fold(
    (0u32, String::new()),
    |(count, mut s), (name, errs)| {
        if *errs > 0 {
            s.push_str(&format!("{name}:{errs} "));
        }
        (count + errs, s)
    },
);
println!("Total errors: {total}, details: {summary}");
// Total errors: 11, details: gpu0:3 gpu2:7 gpu3:1
}
#![allow(unused)]
fn main() {
let readings = [100, 105, 103, 110, 108];

- // Compute deltas between consecutive readings
+ // Compute deltas / 计算连续读数之间的增量
let deltas: Vec<i32> = readings.iter()
    .scan(None::<i32>, |prev, &val| {
        let delta = prev.map(|p| val - p);
        *prev = Some(val);
        Some(delta)
    })
-    .flatten()  // Remove the initial None
+    .flatten()  // Remove initial None / 移除初始的 None
    .collect();
- println!("Deltas: {deltas:?}"); // [5, -2, 7, -2]
+ println!("Deltas: {deltas:?}"); // 结果:[5, -2, 7, -2]
}

-| C++ Pattern | Rust Iterator | Example | +| C++ Pattern / 模式 | Rust Iterator / 迭代器 | Example / 示例 | |––––––––|——————|————| -| for (int i = 0; i < v.size(); i++) | .enumerate() | v.iter().enumerate() | +| for (int i = 0; i < v.size(); i++) | .enumerate() | v.iter().enumerate() | -| Parallel iteration with index | .zip() | a.iter().zip(b.iter()) | -| Parallel iteration with index | .zip() | a.iter().zip(b.iter()) | -| Nested loop → flat result | .flat_map() | vecs.iter().flat_map(\|v\| v.iter()) | -| Nested loop → flat result | .flat_map() | ... | -| Concatenate two containers | .chain() | a.iter().chain(b.iter()) | -| Concatenate two containers | .chain() | ... | -| Sliding window v[i..i+n] | .windows(n) | v.windows(3) | -| Sliding window v[i..i+n] | .windows(n) | v.windows(3) | -| Process in fixed-size groups | .chunks(n) | v.chunks(4) | -| Process in fixed-size groups | .chunks(n) | v.chunks(4) | -| std::accumulate / manual accumulator | .fold() | .fold(init, \|acc, x\| ...) | -| std::accumulate / manual accumulator | .fold() | ... | -| Running total / delta tracking | .scan() | .scan(state, \|s, x\| ...) | -| Running total / delta tracking | .scan() | ... | -| while (it != end && count < n) { ++it; ++count; } | .take(n) | .iter().take(5) | -| while (it != end && count < n) { ++it; ++count; } | .take(n) | ... | -| while (it != end && !pred(*it)) { ++it; } | .skip_while() | .skip_while(\|x\| x < &threshold) | +| while (it != end && !pred(*it)) { ++it; } | .skip_while() | ... | -| std::any_of | .any() | .iter().any(\|x\| x > &limit) | -| std::any_of | .any() | .iter().any(\|x\| x > &limit) | -| std::all_of | .all() | .iter().all(\|x\| x.is_valid()) | -| std::all_of | .all() | .iter().all(\|x\| x.is_valid()) | -| std::none_of | !.any() | !iter.any(\|x\| x.failed()) | -| std::none_of | !.any() | ... | -| std::count_if | .filter().count() | .filter(\|x\| x > &0).count() | -| std::count_if | .filter().count() | ... | -| std::min_element / std::max_element | .min() / .max() | .iter().max()Option<&T> | -| std::min_element / std::max_element | .min() / .max() | ... | -| std::unique | .dedup() (on sorted) | v.dedup() (in-place on Vec) | -| std::unique | .dedup() | ... |

  • Given sensor data as Vec<(String, f64)> (name, temperature), write a **single
  • 给定传感器数据为 Vec<(String, f64)>(名称、温度),编写一个单一的迭代器链,用于:
  • iterator chain** that:
    1. Filters sensors with temp > 80.0
    1. 过滤温度 > 80.0 的传感器
    1. Sorts them by temperature (descending)
    1. 按温度排序(降序)
    1. Formats each as "{name}: {temp}°C [ALARM]"
    1. 将每个格式化为 "{name}: {temp}°C [ALARM]"
    1. Collects into Vec<String>
    1. 收集为 Vec<String>
  • Hint: you’ll need .collect() before .sort_by(), since sorting requires a Vec.
  • 提示:在调用 .sort_by() 之前你需要进行 .collect(),因为排序需要一个 Vec
  • Solution (click to expand)
  • Solution (click to expand) / 解决方案(点击展开)
fn alarm_report(sensors: &[(String, f64)]) -> Vec<String> {
    let mut hot: Vec<_> = sensors.iter()
        .filter(|(_, temp)| *temp > 80.0)
        .collect();
    hot.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
    hot.iter()
        .map(|(name, temp)| format!("{name}: {temp}°C [ALARM]"))
        .collect()
}

fn main() {
    let sensors = vec![
        ("gpu0".to_string(), 72.5),
        ("gpu1".to_string(), 85.3),
        ("gpu2".to_string(), 91.0),
        ("gpu3".to_string(), 78.0),
        ("gpu4".to_string(), 88.7),
    ];
    for line in alarm_report(&sensors) {
        println!("{line}");
    }
}
- // Output:
+ // Output / 输出:
// gpu2: 91°C [ALARM]
// gpu4: 88.7°C [ALARM]
// gpu1: 85.3°C [ALARM]

    • The Iterator trait is used to implement iteration over user defined types (https://doc.rust-lang.org/std/iter/trait.IntoIterator.html)
    • Iterator trait 用于为用户定义的类型实现迭代功能(https://doc.rust-lang.org/std/iter/trait.IntoIterator.html)。
  • - In the example, we'll implement an iterator for the Fibonacci sequence, which starts with 1, 1, 2, ... and the successor is the sum of the previous two numbers
    
  • - 在示例中,我们将为斐波那契数列实现一个迭代器,该数列以 1, 1, 2, ... 开始,后继项是前两项之和。
    
  • - The ```associated type``` in the ```Iterator``` (```type Item = u32;```) defines the output type from our iterator (```u32```)
    
  • - ```Iterator``` 中的 ```associated type```(关联类型,```type Item = u32;```)定义了我们迭代器的输出类型(```u32```)。
    
  • - The ```next()``` method simply contains the logic for implementing our iterator. In this case, all state information is available in the ```Fibonacci``` structure
    
  • - ```next()``` 方法包含了实现迭代器的逻辑。在本例中,所有状态信息都在 ```Fibonacci``` 结构体中。
    
  • - We could have implemented another trait called ```IntoIterator``` to implement the ```into_iter()``` method for more specialized iterators
    
  • - 我们还可以实现另一个名为 ```IntoIterator``` 的 trait,以便为更专门的迭代器实现 ```into_iter()``` 方法。
    
  • - [▶ Try it in the Rust Playground](https://play.rust-lang.org/)
    
  • - [▶ 在 Rust Playground 中尝试](https://play.rust-lang.org/)
    

Iterator Power Tools / 迭代器进阶工具

Iterator Power Tools Reference / 迭代器进阶工具参考

What you’ll learn / 你将学到: Advanced iterator combinators beyond filter/map/collectenumerate, zip, chain, flat_map, scan, windows, and chunks. Essential for replacing C-style indexed for loops with safe, expressive Rust iterators.

filter / map / collect 之外的高级迭代器组合器 —— enumeratezipchainflat_mapscanwindowschunks。这些工具对于用安全、极具表现力的 Rust 迭代器取代 C 风格的索引 for 循环至关重要。

  • The basic filter/map/collect chain covers many cases, but Rust’s iterator library
  • 基础的 filter / map / collect 链条覆盖了许多场景,但 Rust 的迭代器库远比这丰富。
  • is far richer. This section covers the tools you’ll reach for daily — especially when
  • 本节涵盖了你日常会用到的工具 —— 特别是在
  • translating C loops that manually track indices, accumulate results, or process
  • 翻译那些手动跟踪索引、累加结果或按固定大小分块处理数据的 C 风格循环时。

-| Method | C Equivalent | What it does | Returns | +| Method / 方法 | C Equivalent / C 对应物 | What it does / 功能 | Returns / 返回 | |––––|———––|———––|———| -| enumerate() | for (int i=0; ...) | Pairs each element with its index | (usize, T) | +| enumerate() | for (int i=0; ...) | Pairs with index / 将每个元素与其索引配对 | (usize, T) | -| zip(other) | Parallel arrays with same index | Pairs elements from two iterators | (A, B) | +| zip(other) | Parallel arrays / 并行数组 | Pairs from two iterators / 配对两个迭代器的元素 | (A, B) | -| chain(other) | Process array1 then array2 | Concatenates two iterators | T | +| chain(other) | Process seq1 then seq2 | Concatenates two iterators / 连接两个迭代器 | T | -| flat_map(f) | Nested loops | Maps then flattens one level | U | +| flat_map(f) | Nested loops / 嵌套循环 | Maps and flattens / 映射并拍平一层 | U | -| windows(n) | for (int i=0; i<len-n+1; i++) &arr[i..i+n] | Overlapping slices of size n | &[T] | +| windows(n) | Sliding window / 滑动窗口 | Overlapping slices / 大小为 n 的重叠切片 | &[T] | -| chunks(n) | Process n elements at a time | Non-overlapping slices of size n | &[T] | +| chunks(n) | Process n at a time | Fixed-size groups / 大小为 n 的非重叠分块 | &[T] | -| fold(init, f) | int acc = init; for (...) acc = f(acc, x); | Reduce to single value | Acc | +| fold(init, f) | std::accumulate | Reduce to single value / 归约为单个值 | Acc | -| scan(init, f) | Running accumulator with output | Like fold but yields intermediate results | Option<B> | +| scan(init, f) | Running total / 运行总计 | yields intermediate results / 产出中间结果 | Option<B> | -| take(n) / skip(n) | Start loop at offset / limit | First n / skip first n elements | T | +| take(n) / skip(n) | Loop limit / offset | First n / skip first n / 获取前 n 个 / 跳过前 n 个 | T | -| take_while(f) / skip_while(f) | while (pred) {...} | Take/skip while predicate holds | T | +| take_while(f) | while (pred) | Condition-based limit / 基于条件的获取/跳过 | T | -| peekable() | Lookahead with arr[i+1] | Allows .peek() without consuming | T | +| peekable() | Lookahead / 预读 | .peek() without consuming / 在不消耗的情况下预读 | T | -| step_by(n) | for (i=0; i<len; i+=n) | Take every nth element | T | +| step_by(n) | i += n | Take every nth / 每隔 n 个取一个 | T | -| sum() / product() | Accumulate sum/product | Reduce with + or * | T | +| sum() / product() | Sum/Product / 求和/乘积 | Reduce with + or * / 通过 +* 归约 | T | -| any(f) / all(f) | bool found = false; for (...) ... | Short-circuit boolean search | bool | +| any(f) / all(f) | Boolean check / 布尔检查 | Short-circuit search / 短路搜索 | bool |

fn main() {
    let sensors = ["GPU_TEMP", "CPU_TEMP", "FAN_RPM", "PSU_WATT"];

-    // C style: for (int i = 0; i < 4; i++) printf("[%d] %s\n", i, sensors[i]);
+    // C style: for (int i = 0; i < 4; i++) printf("[%d] %s\n", i, sensors[i]); / C 风格循环
    for (i, name) in sensors.iter().enumerate() {
        println!("[{i}] {name}");
    }

-    // Find the index of a specific sensor
+    // Find the index of a specific sensor / 查找特定传感器的索引
    let gpu_idx = sensors.iter().position(|&s| s == "GPU_TEMP");
-    println!("GPU sensor at index: {gpu_idx:?}");  // Some(0)
+    println!("GPU sensor at index: {gpu_idx:?}");  // 结果:Some(0)
}
fn main() {
    let names = ["accel_diag", "nic_diag", "cpu_diag"];
    let statuses = [true, false, true];
    let durations_ms = [1200, 850, 3400];

-    // C: for (int i=0; i<3; i++) printf("%s: %s (%d ms)\n", names[i], ...);
+    // C: for (int i=0; i<3; i++) printf("%s: %s (%d ms)\n", names[i], ...); / C 风格并行数组
    for ((name, passed), ms) in names.iter().zip(&statuses).zip(&durations_ms) {
        let status = if *passed { "PASS" } else { "FAIL" };
        println!("{name}: {status} ({ms} ms)");
    }
}
fn main() {
    let critical = vec!["ECC error", "Thermal shutdown"];
    let warnings = vec!["Link degraded", "Fan slow"];

-    // Process all events in priority order
+    // Process all events in priority order / 按优先级顺序处理所有事件
    let all_events: Vec<_> = critical.iter().chain(warnings.iter()).collect();
    println!("{all_events:?}");
    // ["ECC error", "Thermal shutdown", "Link degraded", "Fan slow"]
}
fn main() {
    let lines = vec!["gpu:42:ok", "nic:99:fail", "cpu:7:ok"];

-    // Extract all numeric values from colon-separated lines
+    // Extract all numeric values from colon-separated lines / 从冒号分隔的行中提取所有数值
    let numbers: Vec<u32> = lines.iter()
        .flat_map(|line| line.split(':'))
        .filter_map(|token| token.parse::<u32>().ok())
        .collect();
-    println!("{numbers:?}");  // [42, 99, 7]
+    println!("{numbers:?}");  // 结果:[42, 99, 7]
}
fn main() {
    let temps = [65, 68, 72, 71, 75, 80, 78, 76];

-    // windows(3): overlapping groups of 3 (like a sliding average)
-    // C: for (int i = 0; i <= len-3; i++) avg(arr[i], arr[i+1], arr[i+2]);
+    // windows(3): overlapping groups of 3 / 3 个一组的重叠分组(类似滑动平均)
+    // C: for (int i = 0; i <= len-3; i++) avg(arr[i], arr[i+1], arr[i+2]); / C 风格滑动窗口
    let moving_avg: Vec<f64> = temps.windows(3)
        .map(|w| w.iter().sum::<i32>() as f64 / 3.0)
        .collect();
    println!("Moving avg: {moving_avg:.1?}");

-    // chunks(2): non-overlapping groups of 2
-    // C: for (int i = 0; i < len; i += 2) process(arr[i], arr[i+1]);
+    // chunks(2): non-overlapping groups of 2 / 2 个一组的非重叠分块
+    // C: for (int i = 0; i < len; i += 2) process(arr[i], arr[i+1]); / C 风格固定间隔
    for pair in temps.chunks(2) {
        println!("Chunk: {pair:?}");
    }

-    // chunks_exact(2): same but panics if remainder exists
+    // chunks_exact(2): same but panics if remainder exists / 同样,但如果存在余数则会 panic
-    // Also: .remainder() gives leftover elements
+    // Also: .remainder() gives leftover elements / 此外:.remainder() 可获取剩余元素
}
fn main() {
    let values = [10, 20, 30, 40, 50];

-    // fold: single final result (like C's accumulator loop)
+    // fold: single final result / fold:最终得到单个结果(类似 C 的累加循环)
    let sum = values.iter().fold(0, |acc, &x| acc + x);
-    println!("Sum: {sum}");  // 150
+    println!("Sum: {sum}");  // 结果:150

-    // Build a string with fold
+    // Build a string with fold / 使用 fold 构建字符串
    let csv = values.iter()
        .fold(String::new(), |acc, x| {
            if acc.is_empty() { format!("{x}") }
            else { format!("{acc},{x}") }
        });
-    println!("CSV: {csv}");  // "10,20,30,40,50"
+    println!("CSV: {csv}");  // 结果:"10,20,30,40,50"

-    // scan: like fold but yields intermediate results
+    // scan: like fold but yields intermediate results / scan:类似 fold 但产出中间结果
    let running_sum: Vec<i32> = values.iter()
        .scan(0, |state, &x| {
            *state += x;
            Some(*state)
        })
        .collect();
-    println!("Running sum: {running_sum:?}");  // [10, 30, 60, 100, 150]
+    println!("Running sum: {running_sum:?}");  // 结果:[10, 30, 60, 100, 150]
}
  • Given raw sensor readings (one per line, format "sensor_name:value:unit"), write an
  • 给定原始传感器读数(每行一条,格式为 "sensor_name:value:unit"),编写一个
  • iterator pipeline that:
    1. Parses each line into (name, f64, unit)
  • 迭代器流水线,用于:
    1. 将每一行解析为 (name, f64, unit)
    1. Filters out readings below a threshold
    1. 过滤掉低于阈值的读数
    1. Groups by sensor name using fold into a HashMap
    1. 使用 fold 按传感器名称分组并存入 HashMap
    1. Prints the average reading per sensor
    1. 打印每个传感器的平均读数
- // Starter code
+ // Starter code / 入门代码
fn main() {
    let raw_data = vec![
        "gpu_temp:72.5:C",
        "cpu_temp:65.0:C",
        "gpu_temp:74.2:C",
        "fan_rpm:1200.0:RPM",
        "cpu_temp:63.8:C",
        "gpu_temp:80.1:C",
        "fan_rpm:1150.0:RPM",
    ];
    let threshold = 70.0;
-    // TODO: Parse, filter values >= threshold, group by name, compute averages
+    // TODO: Parse, filter values >= threshold, group by name, compute averages / 待办:解析、过滤 >= 阈值的值、按名称分组、计算平均值
}
  • Solution (click to expand)
  • Solution (click to expand) / 解决方案(点击展开)
use std::collections::HashMap;

fn main() {
    let raw_data = vec![
        "gpu_temp:72.5:C",
        "cpu_temp:65.0:C",
        "gpu_temp:74.2:C",
        "fan_rpm:1200.0:RPM",
        "cpu_temp:63.8:C",
        "gpu_temp:80.1:C",
        "fan_rpm:1150.0:RPM",
    ];
    let threshold = 70.0;

-    // Parse → filter → group → average
+    // Pipeline: Parse → filter → group → average / 流水线:解析 → 过滤 → 分组 → 平均
    let grouped = raw_data.iter()
        .filter_map(|line| {
            let parts: Vec<&str> = line.splitn(3, ':').collect();
            if parts.len() == 3 {
                let value: f64 = parts[1].parse().ok()?;
                Some((parts[0], value, parts[2]))
            } else {
                None
            }
        })
        .filter(|(_, value, _)| *value >= threshold)
        .fold(HashMap::<&str, Vec<f64>>::new(), |mut acc, (name, value, _)| {
            acc.entry(name).or_default().push(value);
            acc
        });

    for (name, values) in &grouped {
        let avg = values.iter().sum::<f64>() / values.len() as f64;
        println!("{name}: avg={avg:.1} ({} readings)", values.len());
    }
}
- // Output (order may vary):
+ // Output / 输出(顺序可能不同):
// gpu_temp: avg=75.6 (3 readings)
// fan_rpm: avg=1175.0 (2 readings)
    • The Iterator trait is used to implement iteration over user defined types (https://doc.rust-lang.org/std/iter/trait.IntoIterator.html)
    • Iterator trait 用于为用户定义类型实现迭代(https://doc.rust-lang.org/std/iter/trait.IntoIterator.html)。
  • - In the example, we'll implement an iterator for the Fibonacci sequence, which starts with 1, 1, 2, ... and the successor is the sum of the previous two numbers
    
  • - 在示例中,我们将为斐波那契数列实现一个迭代器,该数列以 1, 1, 2, ... 开始,后继项是前两项之和。
    
  • - The ```associated type``` in the ```Iterator``` (```type Item = u32;```) defines the output type from our iterator (```u32```)
    
  • - ```Iterator``` 中的 ```associated type```(关联类型,```type Item = u32;```)定义了我们迭代器的输出类型(```u32```)。
    
  • - The ```next()``` method simply contains the logic for implementing our iterator. In this case, all state information is available in the ```Fibonacci``` structure
    
  • - ```next()``` 方法包含了实现迭代器的逻辑。在本例中,所有状态信息都在 ```Fibonacci``` 结构体中。
    
  • - We could have implemented another trait called ```IntoIterator``` to implement the ```into_iter()``` method for more specialized iterators
    
  • - 我们还可以实现另一个名为 ```IntoIterator``` 的 trait,以便为更专门的迭代器实现 ```into_iter()``` 方法。
    
  • - https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ab367dc2611e1b5a0bf98f1185b38f3f
    
  • - https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ab367dc2611e1b5a0bf98f1185b38f3f
    

Rust concurrency / Rust 并发

What you’ll learn / 你将学到: Rust’s concurrency model — threads, Send/Sync marker traits, Mutex<T>, Arc<T>, channels, and how the compiler prevents data races at compile time. No runtime overhead for thread safety you don’t use.

Rust 的并发模型 —— 线程、Send / Sync 标记 trait、Mutex<T>Arc<T>、信道(channels),以及编译器如何通过编译时检查来防止数据竞态(data races)。对于你未使用的线程安全特性,不会产生任何运行时开销。

  • Rust has built-in support for concurrency, similar to std::thread in C++
    • Rust 内置了对并发的支持,类似于 C++ 中的 std::thread
  • - Key difference: Rust **prevents data races at compile time** through `Send` and `Sync` marker traits
    
  • - 关键区别:Rust 通过 `Send` 和 `Sync` 标记 trait 在**编译时防止数据竞态**。
    
  • - In C++, sharing a `std::vector` across threads without a mutex is UB but compiles fine. In Rust, it won't compile.
    
  • - 在 C++ 中,在没有互斥锁的情况下跨线程共享 `std::vector` 是未定义行为(UB),但编译是正常的。而在 Rust 中,这无法通过编译。
    
  • - `Mutex<T>` in Rust wraps the **data**, not just the access — you literally cannot read the data without locking
    
  • - Rust 中的 `Mutex<T>` 包装的是**数据**本身,而不仅仅是访问权限 —— 字面上,如果不加锁,你甚至无法读取数据。
    
    • The thread::spawn() can be used to create a separate thread that executes the closure || in parallel
    • thread::spawn() 可用于创建一个运行闭包 || 的独立线程。
use std::thread;
use std::time::Duration;
fn main() {
    let handle = thread::spawn(|| {
        for i in 0..10 {
            println!("Count in thread: {i}!");
            thread::sleep(Duration::from_millis(5));
        }
    });

    for i in 0..5 {
        println!("Main thread: {i}");
        thread::sleep(Duration::from_millis(5));
    }

-    handle.join().unwrap(); // The handle.join() ensures that the spawned thread exits
+    handle.join().unwrap(); // Ensures the spawned thread exits / 确保生成的线程已退出
}
    • thread::scope() can be used in cases where it is necessary to borrow from the environment. This works because thread::scope waits until the internal thread returns
    • 在需要从环境中进行借用的情况下,可以使用 thread::scope()。这是可行的,因为 thread::scope 会等待内部线程返回。
    • Try executing this exercise without thread::scope to see the issue
    • 尝试在没有 thread::scope 的情况下执行此练习以查看问题所在。
use std::thread;
fn main() {
  let a = [0, 1, 2];
  thread::scope(|scope| {
      scope.spawn(|| {
          for x in &a {
            println!("{x}");
          }
      });
  });
}

    • We can also use move to transfer ownership to the thread. For Copy types like [i32; 3], the move keyword copies the data into the closure, and the original remains usable
    • 我们还可以使用 move 将所有权转移到线程中。对于像 [i32; 3] 这样实现了 Copy 的类型,move 关键字会将数据拷贝到闭包中,原始变量仍然可用。
use std::thread;
fn main() {
  let mut a = [0, 1, 2];
-  let handle = thread::spawn(move || {
+  let handle = thread::spawn(move || { // 使用 move 关键字
      for x in a {
        println!("{x}");
      }
  });
  a[0] = 42;    // Doesn't affect the copy sent to the thread / 不会影响发送到线程的拷贝
  handle.join().unwrap();
}
    • Arc<T> can be used to share read-only references between multiple threads
    • Arc<T> 可用于在多个线程之间共享只读引用。
  • - ```Arc``` stands for Atomic Reference Counted. The reference isn't released until the reference count reaches 0
    
  • - ```Arc``` 代表“原子引用计数(Atomic Reference Counted)”。直到引用计数达到 0 时,引用才会被释放。
    
  • - ```Arc::clone()``` simply increases the reference count without cloning the data
    
  • - ```Arc::clone()``` 仅增加引用计数,而不克隆数据本身。
    
use std::sync::Arc;
use std::thread;
fn main() {
    let a = Arc::new([0, 1, 2]);
    let mut handles = Vec::new();
    for i in 0..2 {
-        let arc = Arc::clone(&a);
+        let arc = Arc::clone(&a); // 增加引用计数
        handles.push(thread::spawn(move || {
            println!("Thread: {i} {arc:?}");
        }));
    }
    handles.into_iter().for_each(|h| h.join().unwrap());
}
    • Arc<T> can be combined with Mutex<T> to provide mutable references.
    • Arc<T> 可以与 Mutex<T> 结合使用以提供可变引用。
  • - ```Mutex``` guards the protected data and ensures that only the thread holding the lock has access.
    
  • - ```Mutex```(互斥锁)保护受保护的数据,并确保只有持有锁的线程才能访问。
    
  • - The `MutexGuard` is automatically released when it goes out of scope (RAII). Note: `std::mem::forget` can still leak a guard — so "impossible to forget to unlock" is more accurate than "impossible to leak."
    
  • - 当 `MutexGuard` 超出作用域时(RAII),锁会自动释放。注意:`std::mem::forget` 仍然可能导致 guard 泄漏 —— 因此,“不可能忘记解锁”比“不可能泄漏锁”更准确。
    
use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let counter = Arc::new(Mutex::new(0));
    let mut handles = Vec::new();

    for _ in 0..5 {
        let counter = Arc::clone(&counter);
        handles.push(thread::spawn(move || {
-            let mut num = counter.lock().unwrap();
+            let mut num = counter.lock().unwrap(); // 获取锁
            *num += 1;
-            // MutexGuard dropped here — lock released automatically
+            // MutexGuard dropped here / MutexGuard 在此处被丢弃 —— 自动释放锁
        }));
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("Final count: {}", *counter.lock().unwrap());
-    // Output: Final count: 5
+    // Output / 输出:Final count: 5
}
    • RwLock<T> allows multiple concurrent readers or one exclusive writer — the read/write lock pattern from C++ (std::shared_mutex)
    • RwLock<T> 允许多个并发读者一个排他性作者 —— 这是来自 C++ 的读写锁模式(std::shared_mutex)。
  • - Use `RwLock` when reads far outnumber writes (e.g., configuration, caches)
    
  • - 当读取操作远多于写入操作时(例如配置、缓存),请使用 `RwLock`。
    
  • - Use `Mutex` when read/write frequency is similar or critical sections are short
    
  • - 当读写频率相近或临界区很短时,请使用 `Mutex`。
    
use std::sync::{Arc, RwLock};
use std::thread;

fn main() {
    let config = Arc::new(RwLock::new(String::from("v1.0")));
    let mut handles = Vec::new();

-    // Spawn 5 readers — all can run concurrently
+    // Spawn 5 readers — all can run concurrently / 生成 5 个读者 —— 它们可以并发运行
    for i in 0..5 {
        let config = Arc::clone(&config);
        handles.push(thread::spawn(move || {
-            let val = config.read().unwrap();  // Multiple readers OK
+            let val = config.read().unwrap();  // Multiple readers / 允许多个读者
            println!("Reader {i}: {val}");
        }));
    }

-    // One writer — blocks until all readers finish
+    // One writer — blocks until all readers finish / 一个作者 —— 阻塞直到所有读者完成
    {
        let config = Arc::clone(&config);
        handles.push(thread::spawn(move || {
-            let mut val = config.write().unwrap();  // Exclusive access
+            let mut val = config.write().unwrap();  // Exclusive / 排他性访问
            *val = String::from("v2.0");
            println!("Writer: updated to {val}");
        }));
    }

    for handle in handles {
        handle.join().unwrap();
    }
}
    • If a thread panics while holding a Mutex or RwLock, the lock becomes poisoned
    • 如果一个线程在持有 MutexRwLock 时发生 panic,该锁就会变得“中毒(poisoned)”。
  • - Subsequent calls to `.lock()` return `Err(PoisonError)` — the data may be in an inconsistent state
    
  • - 后续对 `.lock()` 的调用将返回 `Err(PoisonError)` —— 因为数据可能处于不一致的状态。
    
  • - You can recover with `.into_inner()` if you're confident the data is still valid
    
  • - 如果你确信数据仍然有效,可以使用 `.into_inner()` 进行恢复。
    
  • - This has no C++ equivalent — `std::mutex` has no poisoning concept; a panicking thread just leaves the lock held
    
  • - 这在 C++ 中没有等价物 —— `std::mutex` 没有中毒概念;发生 panic 的线程只会让锁一直保持被持有状态。
    
use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let data = Arc::new(Mutex::new(vec![1, 2, 3]));

    let data2 = Arc::clone(&data);
    let handle = thread::spawn(move || {
        let mut guard = data2.lock().unwrap();
        guard.push(4);
-        panic!("oops!");  // Lock is now poisoned
+        panic!("oops!");  // Lock is poisoned / 锁在此处中毒
    });

-    let _ = handle.join();  // Thread panicked
+    let _ = handle.join();  // Thread panicked / 线程发生了 panic

-    // Subsequent lock attempts return Err(PoisonError)
+    // Subsequent attempts return Err / 后续尝试将返回错误
    match data.lock() {
        Ok(guard) => println!("Data: {guard:?}"),
        Err(poisoned) => {
            println!("Lock was poisoned! Recovering...");
-            let guard = poisoned.into_inner();  // Access data anyway
+            let guard = poisoned.into_inner();  // Access anyway / 依然访问数据
-            println!("Recovered data: {guard:?}");  // [1, 2, 3, 4] — push succeeded before panic
+            println!("Recovered data: {guard:?}");  // 成功恢复的数据:[1, 2, 3, 4]
        }
    }
}
    • For simple counters and flags, std::sync::atomic types avoid the overhead of a Mutex
    • 对于简单的计数器和标志位,std::sync::atomic 类型可以避免 Mutex 的开销。
  • - `AtomicBool`, `AtomicI32`, `AtomicU64`, `AtomicUsize`, etc.
    
  • - 包括 `AtomicBool`、`AtomicI32`、`AtomicU64`、`AtomicUsize` 等。
    
  • - Equivalent to C++ `std::atomic<T>` — same memory ordering model (`Relaxed`, `Acquire`, `Release`, `SeqCst`)
    
  • - 等价于 C++ 的 `std::atomic<T>` —— 具有相同的内存顺序模型(`Relaxed`、`Acquire`、`Release`、`SeqCst`)。
    
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use std::thread;

fn main() {
    let counter = Arc::new(AtomicU64::new(0));
    let mut handles = Vec::new();

    for _ in 0..10 {
        let counter = Arc::clone(&counter);
        handles.push(thread::spawn(move || {
            for _ in 0..1000 {
-                counter.fetch_add(1, Ordering::Relaxed);
+                counter.fetch_add(1, Ordering::Relaxed); // 原子加法
            }
        }));
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("Counter: {}", counter.load(Ordering::SeqCst));
-    // Output: Counter: 10000
+    // Output / 输出:Counter: 10000
}

-| Primitive | When to use | C++ equivalent | +| Primitive / 原语 | When to use / 何时使用 | C++ equivalent / C++ 等价物 | |———–|———––|––––––––| -| Mutex<T> | General mutable shared state | std::mutex + manual data association | +| Mutex<T> | General mutable shared state / 普通可变共享状态 | std::mutex + 手动数据关联 | -| RwLock<T> | Read-heavy workloads | std::shared_mutex | +| RwLock<T> | Read-heavy workloads / 读多写少的工作负载 | std::shared_mutex | -| Atomic* | Simple counters, flags, lock-free patterns | std::atomic<T> | +| Atomic* | Simple counters, flags / 简单计数器、标志位 | std::atomic<T> | -| Condvar | Wait for a condition to become true | std::condition_variable | +| Condvar | Wait for condition / 等待某个条件成立 | std::condition_variable |

    • Condvar (condition variable) lets a thread sleep until another thread signals that a condition has changed
    • Condvar(条件变量)允许线程睡眠直到另一个线程发出信号表示条件已改变。
  • - Always paired with a `Mutex` — the pattern is: lock, check condition, wait if not ready, act when ready
    
  • - 它总是与 `Mutex` 成对出现 —— 其模式是:加锁、检查条件、如果不就绪则等待、就绪后采取行动。
    
  • - Equivalent to C++ `std::condition_variable` / `std::condition_variable::wait`
    
  • - 等价于 C++ 的 `std::condition_variable` / `std::condition_variable::wait`。
    
  • - Handles **spurious wakeups** — always re-check the condition in a loop (or use `wait_while`/`wait_until`)
    
  • - 处理**虚假唤醒(spurious wakeups)** —— 始终在循环中重新检查条件(或者使用 `wait_while` / `wait_until`)。
    
use std::sync::{Arc, Condvar, Mutex};
use std::thread;

fn main() {
    let pair = Arc::new((Mutex::new(false), Condvar::new()));

-    // Spawn a worker that waits for a signal
+    // Spawn a worker / 生成一个等待信号的工人线程
    let pair2 = Arc::clone(&pair);
    let worker = thread::spawn(move || {
        let (lock, cvar) = &*pair2;
        let mut ready = lock.lock().unwrap();
-        // wait: sleeps until signaled (always re-check in a loop for spurious wakeups)
+        // wait: sleeps until signaled / wait:睡眠直到收到信号(必须在循环中重检)
        while !*ready {
            ready = cvar.wait(ready).unwrap();
        }
        println!("Worker: condition met, proceeding!");
    });

-    // Main thread does some work, then signals the worker
+    // Main thread signals / 主线程发出信号
    thread::sleep(std::time::Duration::from_millis(100));
    {
        let (lock, cvar) = &*pair;
        let mut ready = lock.lock().unwrap();
        *ready = true;
-        cvar.notify_one();  // Wake one waiting thread (notify_all() wakes all)
+        cvar.notify_one();  // Wake one / 唤醒一个等待中的线程
    }

    worker.join().unwrap();
}
  • When to use Condvar vs channels: Use Condvar when threads share mutable state and need to wait for a condition on that state (e.g., “buffer not empty”). Use channels (mpsc) when threads need to pass messages. Channels are generally easier to reason about.

  • 何时使用 Condvar vs 信道(Channels): 当线程间共享可变状态并需要等待该状态满足某个条件(例如,“缓冲区不为空”)时,请使用 Condvar。当线程需要传递消息时,请使用信道(mpsc)。信道通常更容易理解和推理。

    • Rust channels can be used to exchange messages between Sender and Receiver
    • Rust 信道可用于在 发送端(Sender)接收端(Receiver) 之间交换消息。
  • - This uses a paradigm called ```mpsc``` or ```Multi-producer, Single-Consumer```
    
  • - 它采用了一种称为 ```mpsc``` 或 ```多生产者单消费者(Multi-producer, Single-Consumer)``` 的范式。
    
  • - Both ```send()``` and ```recv()``` can block the thread
    
  • - ```send()``` 和 ```recv()``` 都有可能阻塞线程。
    
use std::sync::mpsc;

fn main() {
-    let (tx, rx) = mpsc::channel();
+    let (tx, rx) = mpsc::channel(); // 创建信道
    
    tx.send(10).unwrap();
    tx.send(20).unwrap();
    
    println!("Received: {:?}", rx.recv());
    println!("Received: {:?}", rx.recv());

-    let tx2 = tx.clone();
+    let tx2 = tx.clone(); // 克隆发送端
    tx2.send(30).unwrap();
    println!("Received: {:?}", rx.recv());
}
    • Channels can be combined with threads
    • 信道可以与线程结合使用:
use std::sync::mpsc;
use std::thread;
use std::time::Duration;

fn main() {
    let (tx, rx) = mpsc::channel();
    for _ in 0..2 {
        let tx2 = tx.clone();
        thread::spawn(move || {
            let thread_id = thread::current().id();
            for i in 0..10 {
                tx2.send(format!("Message {i}")).unwrap();
                println!("{thread_id:?}: sent Message {i}");
            }
            println!("{thread_id:?}: done");
        });
    }

-        // Drop the original sender so rx.iter() terminates when all cloned senders are dropped
+    // Drop the original sender / 丢弃原始发送端,以便 rx.iter() 结束
    drop(tx);

    thread::sleep(Duration::from_millis(100));

    for msg in rx.iter() {
        println!("Main: got {msg}");
    }
}
    • Rust uses two marker traits to enforce thread safety at compile time:
    • Rust 使用两个标记 trait 在编译时强制执行线程安全:
  • - `Send`: A type is `Send` if it can be safely **transferred** to another thread
    
  • - `Send`:如果一个类型可以安全地**转移**到另一个线程,则该类型是 `Send`。
    
  • - `Sync`: A type is `Sync` if it can be safely **shared** (via `&T`) between threads
    
  • - `Sync`:如果一个类型可以安全地在线程间**共享**(通过 `&T`),则该类型是 `Sync`。
    
    • Most types are automatically Send + Sync. Notable exceptions:
    • 大多数类型都是自动满足 Send + Sync 的。值得注意的例外包括:
  • - `Rc<T>` is **neither** Send nor Sync (use `Arc<T>` for threads)
    
  • - `Rc<T>` **既不是** Send 也不是 Sync(线程中请使用 `Arc<T>`)。
    
  • - `Cell<T>` and `RefCell<T>` are **not** Sync (use `Mutex<T>` or `RwLock<T>`)
    
  • - `Cell<T>` 和 `RefCell<T>` **不是** Sync(请使用 `Mutex<T>` 或 `RwLock<T>`)。
    
  • - Raw pointers (`*const T`, `*mut T`) are **neither** Send nor Sync
    
  • - 裸指针(`*const T`、`*mut T`)**既不是** Send 也不是 Sync。
    
    • This is why the compiler stops you from using Rc<T> across threads – it literally doesn’t implement Send
    • 这就是为什么编译器会阻止你跨线程使用 Rc<T> —— 它字面上就没有实现 Send
    • Arc<Mutex<T>> is the thread-safe equivalent of Rc<RefCell<T>>
    • Arc<Mutex<T>>Rc<RefCell<T>> 的线程安全对应物。
  • Intuition (Jon Gjengset): Think of values as toys.

  • 直观理解 (Jon Gjengset):把值想象成玩具。

  • Send = you can give your toy away to another child (thread) — transferring ownership is safe.

  • Send = 你可以把玩具送给另一个孩子(线程)—— 转移所有权是安全的。

  • Sync = you can let others play with your toy at the same time — sharing a reference is safe.

  • Sync = 你可以让别人同时玩你的玩具 —— 共享引用是安全的。

  • An Rc<T> has a fragile (non-atomic) reference counter; handing it off or sharing it would corrupt the count, so it is neither Send nor Sync.

  • Rc<T> 的引用计数是脆弱的(非原子性的);转手或共享它都会破坏计数,因此它既不是 Send 也不是 Sync

  • 🔴 Challenge — combines threads, Arc, Mutex, and HashMap
  • 🔴 Challenge / 挑战题 —— 综合运用线程、Arc、Mutex 和 HashMap
    • Given a Vec<String> of text lines, spawn one thread per line to count the words in that line
    • 给定一个存储文本行的 Vec<String>,为每一行生成一个线程来统计该行中的单词。
    • Use Arc<Mutex<HashMap<String, usize>>> to collect results
    • 使用 Arc<Mutex<HashMap<String, usize>>> 来收集结果。
    • Print the total word count across all lines
    • 打印所有行中的总词数。
    • Bonus: Try implementing this with channels (mpsc) instead of shared state
    • 加分项:尝试用信道(mpsc)而非共享状态来实现。
  • Solution (click to expand)
  • Solution (click to expand) / 解决方案(点击展开)
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let lines = vec![
        "the quick brown fox".to_string(),
        "jumps over the lazy dog".to_string(),
        "the fox is quick".to_string(),
    ];

    let word_counts: Arc<Mutex<HashMap<String, usize>>> =
        Arc::new(Mutex::new(HashMap::new()));

    let mut handles = vec![];
    for line in &lines {
        let line = line.clone();
        let counts = Arc::clone(&word_counts);
        handles.push(thread::spawn(move || {
            for word in line.split_whitespace() {
                let mut map = counts.lock().unwrap();
                *map.entry(word.to_lowercase()).or_insert(0) += 1;
            }
        }));
    }

    for handle in handles {
        handle.join().unwrap();
    }

    let counts = word_counts.lock().unwrap();
    let total: usize = counts.values().sum();
    println!("Word frequencies: {counts:#?}");
-    println!("Total words: {total}");
+    println!("Total words: {total}"); // 总词数
}
- // Output:
+ // Output / 输出(顺序可能不同):
// Word frequencies: {
//     "the": 3,
//     "quick": 2,
//     "brown": 1,
//     "fox": 2,
//     "jumps": 1,
//     "over": 1,
//     "lazy": 1,
//     "dog": 1,
//     "is": 1,
// }
// Total words: 13

14. Unsafe Rust and FFI / 14. Unsafe Rust 与 FFI

Unsafe Rust / Unsafe Rust

What you’ll learn / 你将学到: When and how to use unsafe — raw pointer dereferencing, FFI (Foreign Function Interface) for calling C from Rust and vice versa, CString/CStr for string interop, and how to write safe wrappers around unsafe code.

何时以及如何使用 unsafe —— 裸指针解引用、用于 Rust 与 C 互调的 FFI(外部函数接口)、用于字符串互操作的 CString / CStr,以及如何编写由于 unsafe 代码构成的安全包装层。

  • unsafe unlocks access to features that are normally disallowed by the Rust compiler
    • unsafe 解锁了通常被 Rust 编译器禁止的功能访:
  • - Dereferencing raw pointers
    
  • - 解引用裸指针
    
  • - Accessing *mutable* static variables
    
  • - 访问**可变**静态变量
    
  • - https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html
    
  • - 相关文档:https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html
    
    • With great power comes great responsibility
    • 权力越大,责任越大:
  • - ```unsafe``` tells the compiler "I, the programmer, take responsibility for upholding the invariants that the compiler normally guarantees"
    
  • - ```unsafe``` 告诉编译器:“我,作为程序员,负责维护编译器通常保证的不变量。”
    
  • - Must guarantee no aliased mutable and immutable references, no dangling pointers, no invalid references, ...
    
  • - 必须确保没有同时存在的可变与不可变引用别名、没有悬挂指针、没有无效引用等等。
    
  • - The use of ```unsafe``` should be limited to the smallest possible scope
    
  • - ```unsafe``` 的使用应限制在尽可能小的范围内。
    
  • - All code using ```unsafe``` should have a "safety" comment describing the assumptions
    
  • - 所有使用 ```unsafe``` 的代码都应包含一个“safety”注释,说明其所依赖的假设。
    
unsafe fn harmless() {}
fn main() {
-    // Safety: We are calling a harmless unsafe function
+    // Safety: We are calling a harmless unsafe function / 安全说明:我们正在调用一个无害的 unsafe 函数
    unsafe {
        harmless();
    }
    let a = 42u32;
    let p = &a as *const u32;
-    // Safety: p is a valid pointer to a variable that will remain in scope
+    // Safety: p is a valid pointer / 安全说明:p 是一个指向仍在作用域内的变量的有效指针
    unsafe {
        println!("{}", *p);
    }
-    // Safety: Not safe; for illustration purposes only
+    // Safety: Not safe / 安全说明:此处并不安全;仅用于演示
    let dangerous_buffer = 0xb8000 as *mut u32;
    unsafe {
        println!("About to go kaboom!!!");
-        *dangerous_buffer = 0; // This will SEGV on most modern machines
+        *dangerous_buffer = 0; // SEGV expected / 这在大多数现代机器上会导致段错误(SEGV)
    }
}
  • FFI stands for Foreign Function Interface — the mechanism Rust uses to call functions written in other languages (such as C) and vice versa.
  • FFI 代表外部函数接口(Foreign Function Interface) —— 这是 Rust 用于调用其他语言(如 C)编写的函数,以及反向调用的机制。
  • When interfacing with C code, Rust’s String and &str types (which are UTF-8 without null terminators) aren’t directly compatible with C strings (which are null-terminated byte arrays). Rust provides CString (owned) and CStr (borrowed) from std::ffi for this purpose:
  • 在与 C 代码接口时,Rust 的 String&str 类型(不带空终止符的 UTF-8 编码)与 C 字符串(以空字符终止的字节数组)不直接兼容。为此,Rust 在 std::ffi 中提供了 CString(拥有所有权)和 CStr(借用):

-| Type | Analogous to | Use when | +| Type / 类型 | Analogous to / 类似于 | Use when / 使用场景 | |——|———––|–––––| -| CString | String (owned) | Creating a C string from Rust data | +| CString | String (owned) | Creating C string from Rust / 从 Rust 数据创建 C 字符串 | -| &CStr | &str (borrowed) | Receiving a C string from foreign code | +| &CStr | &str (borrowed) | Receiving C string from foreign / 从外部代码接收 C 字符串 |

#![allow(unused)]
fn main() {
use std::ffi::{CString, CStr};
use std::os::raw::c_char;

fn demo_ffi_strings() {
-    // Creating a C-compatible string (adds null terminator)
+    // Creating a C-compatible string / 创建 C 兼容字符串(添加空终止符)
    let c_string = CString::new("Hello from Rust").expect("CString::new failed");
    let ptr: *const c_char = c_string.as_ptr();

-    // Converting a C string back to Rust (unsafe because we trust the pointer)
-    // Safety: ptr is valid and null-terminated (we just created it above)
+    // Converting back to Rust / 将 C 字符串转回 Rust(unsafe,因为我们信任该指针)
+    // Safety: ptr is valid / 安全说明:ptr 有效且以空字符结尾(我们刚刚创建了它)
    let back_to_rust: &CStr = unsafe { CStr::from_ptr(ptr) };
    let rust_str: &str = back_to_rust.to_str().expect("Invalid UTF-8");
    println!("{}", rust_str);
}
}
  • Warning: CString::new() will return an error if the input contains interior null bytes (\0). Always handle the Result. You’ll see CStr used extensively in the FFI examples below.

  • 警告:如果输入内容包含内部空字节(\0),CString::new() 将返回错误。请务必处理 Result。在下面的 FFI 示例中,你将看到 CStr 的大量应用。

    • FFI methods must be marked with #[no_mangle] to ensure that the compiler doesn’t mangle the name
    • FFI 方法必须标记为 #[no_mangle],以确保编译器不会对名称进行“混淆(mangle)”。
    • We’ll compile the crate as a static library
    • 我们将把该 crate 编译为一个静态库。
      #![allow(unused)]
      fn main() {
      #[no_mangle] 
      pub extern "C" fn add(left: u64, right: u64) -> u64 {
          left + right
      }
      }
    • We’ll compile the following C-code and link it against our static library.
    • 我们将编译以下 C 代码,并将其与我们的静态库链接。
      #include <stdio.h>
      #include <stdint.h>
      extern uint64_t add(uint64_t, uint64_t);
      int main() {
          printf("Add returned %llu\n", add(21, 21));
      }
      
    • In the following examples, we’ll create a Rust logging interface and expose it to
  • [PYTHON] and C
    • 在接下来的示例中,我们将创建一个 Rust 日志接口,并将其暴露给 PythonC
  • - We'll see how the same interface can be used natively from Rust and C
    
  • - 我们将了解同一个接口如何被 Rust 和 C 原生使用。
    
  • - We will explore the use of tools like ```cbindgen``` to generate header files for ```C```
    
  • - 我们将探索使用 ```cbindgen``` 之类的工具为 ```C``` 生成头文件。
    
  • - We will see how ```unsafe``` wrappers can act as a bridge to safe Rust code
    
  • - 我们将了解如何通过 ```unsafe``` 包装器作为通往安全 Rust 代码的桥梁。
    
#![allow(unused)]
fn main() {
fn create_or_open_log_file(log_file: &str, overwrite: bool) -> Result<File, String> {
    if overwrite {
        File::create(log_file).map_err(|e| e.to_string())
    } else {
        OpenOptions::new()
            .write(true)
            .append(true)
            .open(log_file)
            .map_err(|e| e.to_string())
    }
}

fn log_to_file(file_handle: &mut File, message: &str) -> Result<(), String> {
    file_handle
        .write_all(message.as_bytes())
        .map_err(|e| e.to_string())
}
}
#![allow(unused)]
fn main() {
struct SimpleLogger {
    log_level: LogLevel,
    file_handle: File,
}

impl SimpleLogger {
    fn new(log_file: &str, overwrite: bool, log_level: LogLevel) -> Result<Self, String> {
        let file_handle = create_or_open_log_file(log_file, overwrite)?;
        Ok(Self {
            file_handle,
            log_level,
        })
    }

    fn log_message(&mut self, log_level: LogLevel, message: &str) -> Result<(), String> {
        if log_level as u32 <= self.log_level as u32 {
            let timestamp = Local::now().format("%Y-%m-%d %H:%M:%S").to_string();
            let message = format!("Simple: {timestamp} {log_level} {message}\n");
            log_to_file(&mut self.file_handle, &message)
        } else {
            Ok(())
        }
    }
}
}
    • Testing functionality with Rust is trivial
    • 使用 Rust 测试功能非常简单:
  • - Test methods are decorated with ```#[test]```, and aren't part of the compiled binary 
    
  • - 测试方法使用 ```#[test]``` 装饰,不属于编译后的二进制文件。
    
  • - It's easy to create mock methods for testing purposes
    
  • - 为测试目的创建 mock(模拟)方法非常容易。
    
#![allow(unused)]
fn main() {
#[test]
fn testfunc() -> Result<(), String> {
    let mut logger = SimpleLogger::new("test.log", false, LogLevel::INFO)?;
    logger.log_message(LogLevel::TRACELEVEL1, "Hello world")?;
    logger.log_message(LogLevel::CRITICAL, "Critical message")?;
-    Ok(()) // The compiler automatically drops logger here
+    Ok(()) // Automatically drops / 编译器在此处自动释放 logger
}
}
cargo test
    • cbindgen is a great tool for generating header files for exported Rust functions
    • cbindgen 是为导出的 Rust 函数生成头文件的绝佳工具。
  • - Can be installed using cargo
    
  • - 可以使用 cargo 安装:
    
cargo install cbindgen
cbindgen 
    • Function and structures can be exported using #[no_mangle] and #[repr(C)]
    • 函数和结构体可以使用 #[no_mangle]#[repr(C)] 导出。
  • - We'll assume the common interface pattern passing in a `**` to the actual implementation and returning 0 on success and non-zero on error
    
  • - 我们将采用通用的接口模式:向实际实现传递一个指向指针的指针(`**`),成功时返回 0,出错时返回非零值。
    
  • - **Opaque vs transparent structs**: Our `SimpleLogger` is passed as an *opaque pointer* (`*mut SimpleLogger`) — the C side never accesses its fields, so `#[repr(C)]` is **not** needed. Use `#[repr(C)]` when C code needs to read/write struct fields directly:
    
  • - **不透明(Opaque)与透明结构体**:我们的 `SimpleLogger` 是作为**不透明指针**(`*mut SimpleLogger`)传递的 —— C 侧永远不会访问其字段,因此**不需要** `#[repr(C)]`。只有当 C 代码需要直接读写结构体字段时,才必须使用 `#[repr(C)]`:
    
#![allow(unused)]
fn main() {
-// Opaque — C only holds a pointer, never inspects fields. No #[repr(C)] needed.
+// Opaque — C holds pointer only / 不透明 —— C 仅持有指针,不检查字段。无需 #[repr(C)]。
struct SimpleLogger { /* Rust-only fields */ }

-// Transparent — C reads/writes fields directly. MUST use #[repr(C)].
+// Transparent — C accesses fields / 透明 —— C 直接读写字段。必须使用 #[repr(C)]。
#[repr(C)]
pub struct Point {
    pub x: f64,
    pub y: f64,
}
}
typedef struct SimpleLogger SimpleLogger;
uint32_t create_simple_logger(const char *file_name, struct SimpleLogger **out_logger);
uint32_t log_entry(struct SimpleLogger *logger, const char *message);
uint32_t drop_logger(struct SimpleLogger *logger);
    • Note that we need to a lot of sanity checks
    • 请注意,我们需要进行大量的健全性检查。
    • We have to explicitly leak memory to prevent Rust from automatically deallocating
    • 我们必须显式地进行内存“泄漏(leak)”,以防止 Rust 自动释放内存。
#![allow(unused)]
fn main() {
#[no_mangle] 
pub extern "C" fn create_simple_logger(file_name: *const std::os::raw::c_char, out_logger: *mut *mut SimpleLogger) -> u32 {
    use std::ffi::CStr;
-    // Make sure pointer isn't NULL
+    // Make sure pointer is not NULL / 确保指针不是 NULL
    if file_name.is_null() || out_logger.is_null() {
        return 1;
    }
-    // Safety: The passed in pointer is either NULL or 0-terminated by contract
+    // Safety / 安全说明:根据契约,传入的指针必然以空字符结尾
    let file_name = unsafe {
        CStr::from_ptr(file_name)
    };
    let file_name = file_name.to_str();
-    // Make sure that file_name doesn't have garbage characters
+    // Check for garbage / 确保文件名中没有乱码
    if file_name.is_err() {
        return 1;
    }
    let file_name = file_name.unwrap();
-    // Assume some defaults; we'll pass them in in real life
+    // Use defaults / 假定一些默认值;现实中我们会把它们传进来
    let new_logger = SimpleLogger::new(file_name, false, LogLevel::CRITICAL);
-    // Check that we were able to construct the logger
+    // Check construction / 检查是否能成功构造 logger
    if new_logger.is_err() {
        return 1;
    }
    let new_logger = Box::new(new_logger.unwrap());
-    // This prevents the Box from being dropped when if goes out of scope
+    // Leak the Box / 防止 Box 在超出作用域时被释放
    let logger_ptr: *mut SimpleLogger = Box::leak(new_logger);
-    // Safety: logger is non-null and logger_ptr is valid
+    // Safety / 安全说明:logger 非空且 logger_ptr 有效
    unsafe {
        *out_logger = logger_ptr;
    }
    return 0;
}
}
    • We have similar error checks in log_entry()
    • 我们在 log_entry() 中也有类似的错误检查。
#![allow(unused)]
fn main() {
#[no_mangle]
pub extern "C" fn log_entry(logger: *mut SimpleLogger, message: *const std::os::raw::c_char) -> u32 {
    use std::ffi::CStr;
    if message.is_null() || logger.is_null() {
        return 1;
    }
-    // Safety: message is non-null
+    // Safety / 安全说明:message 非空
    let message = unsafe {
        CStr::from_ptr(message)
    };
    let message = message.to_str();
-    // Make sure that file_name doesn't have garbage characters
+    // Check for garbage / 确保消息中没有乱码
    if message.is_err() {
        return 1;
    }
-    // Safety: logger is valid pointer previously constructed by create_simple_logger()
+    // Safety: previously constructed / 安全说明:logger 是由 create_simple_logger() 构造的有效指针
    unsafe {
        (*logger).log_message(LogLevel::CRITICAL, message.unwrap()).is_err() as u32
    }
}

#[no_mangle]
pub extern "C" fn drop_logger(logger: *mut SimpleLogger) -> u32 {
    if logger.is_null() {
        return 1;
    }
-    // Safety: logger is valid pointer previously constructed by create_simple_logger()
+    // Safety: previously constructed / 安全说明:logger 是由 create_simple_logger() 构造的有效指针
    unsafe {
-        // This constructs a Box<SimpleLogger>, which is dropped when it goes out of scope
+        // Re-construct the Box / 重新构造 Box<SimpleLogger>,在其超出作用域时会被释放
        let _ = Box::from_raw(logger);
    }
    0
}
}
    • We can test our (C)-FFI using Rust, or by writing a (C)-program
    • 我们可以使用 Rust 或编写 C 程序来测试我们的 (C)-FFI。
#![allow(unused)]
fn main() {
#[test]
fn test_c_logger() {
-    // The c".." creates a NULL terminated string
+    // c".." creates NULL terminated / c".." 会创建一个空终止的字符串
    let file_name = c"test.log".as_ptr() as *const std::os::raw::c_char;
    let mut c_logger: *mut SimpleLogger = std::ptr::null_mut();
    assert_eq!(create_simple_logger(file_name, &mut c_logger), 0);
-    // This is the manual way to create c"..." strings
+    // Manual way for null termination / 带有空终止符的手动方式
    let message = b"message from C\0".as_ptr() as *const std::os::raw::c_char;
    assert_eq!(log_entry(c_logger, message), 0);
    drop_logger(c_logger);
}
}
#include "logger.h"
// ...
int main() {
    SimpleLogger *logger = NULL;
    if (create_simple_logger("test.log", &logger) == 0) {
        log_entry(logger, "Hello from C");
-        drop_logger(logger); /*Needed to close handle, etc.*/
+        drop_logger(logger); /* Close handle / 需要关闭句柄等 */
    } 
-    ...
+    // ...
}
  • The TL;DR version is that using unsafe requires deliberate thought
  • 简而言之,使用 unsafe 需要深思熟虑:
  • - Always document the safety assumptions made by the code and review it with experts
    
  • - 始终记录代码所做的安全假设,并请专家进行审查。
    
  • - Use tools like cbindgen, Miri, Valgrind that can help verify correctness
    
  • - 使用 cbindgen、Miri、Valgrind 等有助于验证正确性的工具。
    
  • - **Never let a panic unwind across an FFI boundary** — this is UB. Use `std::panic::catch_unwind` at FFI entry points, or configure `panic = "abort"` in your profile
    
  • - **绝不允许 panic 跨越 FFI 边界传播** —— 这是未定义行为。在 FFI 入口点使用 `std::panic::catch_unwind`,或者在配置文件中配置 `panic = "abort"`。
    
  • - If a struct is shared across FFI, mark it `#[repr(C)]` to guarantee C-compatible memory layout
    
  • - 如果结构体是在 FFI 间共享的,请将其标记为 `#[repr(C)]` 以保证 C 兼容的内存布局。
    
  • - Consult https://doc.rust-lang.org/nomicon/intro.html (the "Rustonomicon" — the dark arts of unsafe Rust)
    
  • - 参考 https://doc.rust-lang.org/nomicon/intro.html(《Rustonomicon》 —— 深入 unsafe Rust 的黑魔法)。
    
  • - Seek help of internal experts
    
  • - 寻求内部专家的帮助。
    
  • C++ developers are familiar with Valgrind and sanitizers. Rust has those plus Miri, which is far more precise for Rust-specific UB:
  • C++ 开发者对 Valgrind 和 sanitizers 很熟悉。Rust 不仅有这些,还有 Miri,它对于 Rust 特有的未定义行为更加精确:

-| | Miri | Valgrind | C++ sanitizers (ASan/MSan/UBSan) | +| Feature / 特性 | Miri | Valgrind | C++ sanitizers | |—|———|———––|––––––––––––––| -| What it catches | Rust-specific UB: stacked borrows, invalid enum discriminants, uninitialized reads, aliasing violations | Memory leaks, use-after-free, invalid reads/writes, uninitialized memory | Buffer overflow, use-after-free, data races, UB | +| What it catches / 捕获内容 | Rust-specific UB / Rust 特有 UB | Memory leaks / 内存泄漏等 | Buffer overflow / 缓冲区溢出等 | -| How it works | Interprets MIR (Rust’s mid-level IR) — no native execution | Instruments compiled binary at runtime | Compile-time instrumentation | +| Mechanism / 机制 | Interprets MIR / 解释 MIR | Runtime instr. / 运行时插桩 | Compile instr. / 编译时插桩 | -| FFI support | ❌ Cannot cross FFI boundary (skips C calls) | ✅ Works on any compiled binary, including FFI | ✅ Works if C code also compiled with sanitizers | +| FFI support / FFI 支持 | ❌ NO / 否 | ✅ YES / 是 | ✅ YES / 是 | -| Speed | ~100x slower than native | ~10-50x slower | ~2-5x slower | +| Speed / 速度 | ~100x slower | ~10-50x slower | ~2-5x slower | -| When to use | Pure Rust unsafe code, data structure invariants | FFI code, full binary integration tests | C/C++ side of FFI, performance-sensitive testing | +| When to use / 适用场景 | Pure Rust / 纯 Rust unsafe | FFI / 全局集成测试 | C/C++ 端的 FFI 检测 | -| Catches aliasing bugs | ✅ Stacked Borrows model | ❌ | Partially (TSan for data races) | +| Aliasing bugs / 别名缺陷 | ✅ YES / 是 | ❌ NO / 否 | Partially / 部分 |

  • Recommendation: Use both — Miri for pure Rust unsafe, Valgrind for FFI integration:
  • 建议:双管齐下 —— 对纯 Rust unsafe 代码使用 Miri,对 FFI 集成使用 Valgrind:
    • Miri — catches Rust-specific UB that Valgrind cannot see (aliasing violations, invalid enum values, stacked borrows):
    • Miri —— 捕获 Valgrind 无法识别的 Rust 特有 UB(别名冲突、无效枚举值、stacked borrows):
      rustup +nightly component add miri
      cargo +nightly miri test                    # Run all tests / 运行所有测试
      cargo +nightly miri test -- test_name       # Run specific / 运行特定测试
      
  • > ⚠️ Miri requires nightly and cannot execute FFI calls. Isolate unsafe Rust logic into testable units.
    
  • > ⚠️ Miri 需要 nightly 版本且无法执行 FFI 调用。请将 unsafe Rust 逻辑隔离为可测试的单元。
    
    • Valgrind — the tool you already know, works on the compiled binary including FFI:
    • Valgrind —— 你熟悉的工具,作用于包括 FFI 在内的已编译二进制文件:
      sudo apt install valgrind
      cargo install cargo-valgrind
      cargo valgrind test                         # Run all / 运行所有测试
      
  • > Catches leaks in `Box::leak` / `Box::from_raw` patterns common in FFI code.
    
  • > 捕获 FFI 代码中常见的 `Box::leak` / `Box::from_raw` 模式下的泄露。
    
    • cargo-careful — runs tests with extra runtime checks enabled (between regular tests and Miri):
    • cargo-careful —— 运行时执行额外的安全检查(由于普通测试和 Miri 之间):
      cargo install cargo-careful
      cargo +nightly careful test
      
    • cbindgen is a great tool for (C) FFI to Rust
    • cbindgen 是实现 (C) FFI 到 Rust 的利器。
  • - Use ```bindgen``` for FFI-interfaces in the other direction (consult the extensive documentation)
    
  • - 另一个方向的 FFI 接口请使用 **bindgen**(详见其丰富文档)。
    
    • Do not assume that your unsafe code is correct, or that it’s fine to use from safe Rust. It’s really easy to make mistakes, and even code that seemingly works correctly can be wrong for subtle reasons
    • 不要假定你的 unsafe 代码是正确的,或者它可以直接从安全 Rust 中调用。犯错非常容易,即使是表面上运行正常的代码也可能因为细微原因而存在错误。
  • - Use tools to verify correctness
    
  • - 使用工具验证正确性。
    
  • - If still in doubt, reach out for expert advice
    
  • - 如有疑虑,请寻求专家建议。
    
    • Make sure that your unsafe code has comments with an explicit documentation about assumptions and why it’s correct
    • 确保你的 unsafe 代码带有明确记录假设及正确性理由的注释。
  • - Callers of ```unsafe``` code should have corresponding comments on safety as well, and observe restrictions
    
    • unsafe 代码的调用者也应当有相应的安全注释,并遵守相关限制。
  • 🔴 Challenge — requires understanding unsafe blocks, raw pointers, and safe API design
  • 🔴 Challenge / 挑战题 —— 需要理解 unsafe 块、裸指针和安全 API 设计
    • Write a safe Rust wrapper around an unsafe FFI-style function. The exercise simulates calling a C function that writes a formatted string into a caller-provided buffer.
    • 为一个 unsafe 的 FFI 风格函数编写一个安全的 Rust 包装器。本练习模拟调用一个 C 函数,该函数将格式化字符串写入调用者提供的缓冲区。
    • Step 1: Implement the unsafe function unsafe_greet that writes a greeting into a raw *mut u8 buffer
    • 步骤 1:实现 unsafe 函数 unsafe_greet,它将问候语写入一个裸 *mut u8 缓冲区。
    • Step 2: Write a safe wrapper safe_greet that allocates a Vec<u8>, calls the unsafe function, and returns a String
    • 步骤 2:编写一个安全包装器 safe_greet,它分配一个 Vec<u8>,调用该 unsafe 函数,并返回一个 String
    • Step 3: Add proper // Safety: comments to every unsafe block
    • 步骤 3:为每个 unsafe 块添加妥当的 // Safety: 注释。
  • Starter code:
  • Starter code / 入门代码:
use std::fmt::Write as _;

- /// Simulates a C function: writes "Hello, <name>!" into buffer.
+ /// Simulates a C function / 模拟 C 函数:将 "Hello, <name>!" 写入缓冲区
/// Returns the number of bytes written (excluding null terminator).
/// # Safety
/// - `buf` must point to at least `buf_len` writable bytes
/// - `name` must be a valid pointer to a null-terminated C string
unsafe fn unsafe_greet(buf: *mut u8, buf_len: usize, name: *const u8) -> isize {
-    // TODO: Build greeting, copy bytes into buf, return length
-    // Hint: use std::ffi::CStr::from_ptr or iterate bytes manually
+    // TODO: Build greeting / 待办:构建问候语,将字节拷贝到 buf,返回长度
+    // Hint / 提示:使用 std::ffi::CStr::from_ptr 或手动遍历字节
    todo!()
}

- /// Safe wrapper — no unsafe in the public API
+ /// Safe wrapper / 安全包装器 —— 公开 API 中没有 unsafe
fn safe_greet(name: &str) -> Result<String, String> {
-    // TODO: Allocate a Vec<u8> buffer, create a null-terminated name,
-    // call unsafe_greet inside an unsafe block with Safety comment,
-    // convert the result back to a String
+    // TODO / 待办:分配 Vec<u8> 缓冲区,创建一个以空字符结尾的名称,
+    // 在带有 Safety 注释的 unsafe 块中调用 unsafe_greet,
+    // 将结果转回 String
    todo!()
}

fn main() {
    match safe_greet("Rustacean") {
        Ok(msg) => println!("{msg}"),
        Err(e) => eprintln!("Error: {e}"),
    }
-    // Expected output: Hello, Rustacean!
+    // Expected / 预期输出:Hello, Rustacean!
}
  • Solution (click to expand)
  • Solution (click to expand) / 解决方案(点击展开)
use std::ffi::CStr;

- /// Simulates a C function: writes "Hello, <name>!" into buffer.
- /// Returns the number of bytes written, or -1 if buffer too small.
+ /// Simulates a C function / 模拟 C 函数:写入问候语,返回长度或 -1
/// # Safety
/// - `buf` must point to at least `buf_len` writable bytes
/// - `name` must be a valid pointer to a null-terminated C string
unsafe fn unsafe_greet(buf: *mut u8, buf_len: usize, name: *const u8) -> isize {
-    // Safety: caller guarantees name is a valid null-terminated string
+    // Safety / 安全说明:调用者保证 name 是有效的空终止字符串
    let name_cstr = unsafe { CStr::from_ptr(name as *const std::os::raw::c_char) };
    let name_str = match name_cstr.to_str() {
        Ok(s) => s,
        Err(_) => return -1,
    };
    let greeting = format!("Hello, {}!", name_str);
    if greeting.len() > buf_len {
        return -1;
    }
-    // Safety: buf points to at least buf_len writable bytes (caller guarantee)
+    // Safety / 安全说明:buf 指向至少 buf_len 个可写字节
    unsafe {
        std::ptr::copy_nonoverlapping(greeting.as_ptr(), buf, greeting.len());
    }
    greeting.len() as isize
}

- /// Safe wrapper — no unsafe in the public API
+ /// Safe wrapper / 安全包装器
fn safe_greet(name: &str) -> Result<String, String> {
    let mut buffer = vec![0u8; 256];
-    // Create a null-terminated version of name for the C API
+    // Create null-terminated / 为 C API 创建空终止的名称
    let name_with_null: Vec<u8> = name.bytes().chain(std::iter::once(0)).collect();

-    // Safety: buffer has 256 writable bytes, name_with_null is null-terminated
+    // Safety / 安全说明:buffer 有 256 个可写字节,name_with_null 已空终止
    let bytes_written = unsafe {
        unsafe_greet(buffer.as_mut_ptr(), buffer.len(), name_with_null.as_ptr())
    };

    if bytes_written < 0 {
        return Err("Buffer too small or invalid name".to_string());
    }

    String::from_utf8(buffer[..bytes_written as usize].to_vec())
        .map_err(|e| format!("Invalid UTF-8: {e}"))
}

fn main() {
    match safe_greet("Rustacean") {
        Ok(msg) => println!("{msg}"),
        Err(e) => eprintln!("Error: {e}"),
    }
}
-// Output:
+// Output / 输出:
// Hello, Rustacean!

no_std — Rust Without the Standard Library / no_std —— 不依赖标准库的 Rust

What you’ll learn / 你将学到: How to write Rust for bare-metal and embedded targets using #![no_std] — the core and alloc crate split, panic handlers, and how this compares to embedded C without libc.

如何使用 #![no_std] 为裸机和嵌入式目标编写 Rust 代码 —— corealloc crate 的划分、panic 处理器,以及这与不带 libc 的嵌入式 C 的对比。

  • If you come from embedded C, you’re already used to working without libc or with a minimal
  • 如果你来自嵌入式 C 领域,你已经习惯了在没有 libc 或只有极简运行时的情况下工作。
  • runtime. Rust has a first-class equivalent: the #![no_std] attribute.
  • Rust 有一个一流的等价物:#![no_std] 属性。
  • When you add #![no_std] to the crate root, the compiler removes the
  • 当你在 crate 根部添加 #![no_std] 时,编译器会移除
  • implicit extern crate std; and links only against core (and optionally alloc).
  • 隐式的 extern crate std;,并仅链接到 core(以及可选的 alloc)。

-| Layer | What it provides | Requires OS / heap? | +| Layer / 层级 | What it provides / 提供内容 | Requires OS / heap? / 需要 OS/堆? | |—––|—————–|———————| -| core | Primitive types, Option, Result, Iterator, math, slice, str, atomics, fmt | No — runs on bare metal | +| core | Primitive types, Option, Result, Iterator, math, slice, str, atomics, fmt | No — runs on bare metal / 否 —— 可在裸机运行 | -| alloc | Vec, String, Box, Rc, Arc, BTreeMap | Needs a global allocator, but no OS | +| alloc | Vec, String, Box, Rc, Arc, BTreeMap | Needs allocator / 需要分配器,但无需 OS | -| std | HashMap, fs, net, thread, io, env, process | Yes — needs an OS | +| std | HashMap, fs, net, thread, io, env, process | Yes — needs an OS / 是 —— 需要操作系统 |

  • Rule of thumb for embedded devs: if your C project links against -lc and

  • 嵌入式开发者的经验法则:如果你的 C 项目链接了 -lc 并且

  • uses malloc, you can probably use core + alloc. If it runs on bare metal

  • 使用了 malloc,你大概可以使用 core + alloc。如果它是在没有

  • without malloc, stick with core only.

  • malloc 的情况下在裸机上运行,请坚持仅使用 core

#![allow(unused)]
fn main() {
- // src/lib.rs  (or src/main.rs for a binary with #![no_main])
+ // src/lib.rs  (or src/main.rs for a binary with #![no_main] / 或用于带 #![no_main] 的二进制)
#![no_std]

- // You still get everything in `core`:
+ // You still get everything in `core` / 你仍然拥有 core 中的一切:
use core::fmt;
use core::result::Result;
use core::option::Option;

- // If you have an allocator, opt in to heap types:
+ // If you have an allocator / 如果你有分配器,可以启用堆类型:
extern crate alloc;
use alloc::vec::Vec;
use alloc::string::String;
}
  • For a bare-metal binary you also need #![no_main] and a panic handler:
  • 对于裸机二进制文件,你还需要 #![no_main] 和一个 panic 处理器:
#![allow(unused)]
#![no_std]
#![no_main]

fn main() {
use core::panic::PanicInfo;

#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
-    loop {} // hang on panic — replace with your board's reset/LED blink
+    loop {} // hang on panic / 发生 panic 时挂起 —— 替换为你开发板的复位/LED 闪烁代码
}

- // Entry point depends on your HAL / linker script
+ // Entry point / 入口点取决于你的 HAL / 链接脚本
}

-| std feature | no_std alternative | +| std feature / std 特性 | no_std alternative / no_std 替代方案 | |—————|———————| -| println! | core::write! to a UART / defmt | +| println! | core::write! to UART / defmt | -| HashMap | heapless::FnvIndexMap (fixed capacity) or BTreeMap (with alloc) | +| HashMap | heapless::FnvIndexMap (fixed) or BTreeMap | -| Vec | heapless::Vec (stack-allocated, fixed capacity) | +| Vec | heapless::Vec (stack-allocated / 栈分配) | -| String | heapless::String or &str | +| String | heapless::String or &str | -| std::io::Read/Write | embedded_io::Read/Write | +| std::io::Read/Write | embedded_io::Read/Write | -| thread::spawn | Interrupt handlers, RTIC tasks | +| thread::spawn | Interrupts / 中断处理器、RTIC 任务 | -| std::time | Hardware timer peripherals | +| std::time | Hardware timers / 硬件定时器外设 | -| std::fs | Flash / EEPROM drivers | +| std::fs | Flash / EEPROM drivers / 驱动程序 |

-| Crate | Purpose | Notes | +| Crate | Purpose / 用途 | Notes / 说明 | |—––|———|—––| -| heapless | Fixed-capacity Vec, String, Queue, Map | No allocator needed — all on the stack | +| heapless | Fixed-capacity collections / 固定容量集合 | No allocator / 无需分配器 —— 全在栈上 | -| defmt | Efficient logging over probe/ITM | Like printf but deferred formatting on the host | +| defmt | Efficient logging / 高效日志 | Like printf / 类似 printf 但在主机端延迟格式化 | -| embedded-hal | Hardware abstraction traits (SPI, I²C, GPIO, UART) | Implement once, run on any MCU | +| embedded-hal | HW abstraction / 硬件抽象 Trait | Implement once / 一次实现,处处运行 | -| cortex-m | ARM Cortex-M intrinsics & register access | Low-level, like CMSIS | +| cortex-m | ARM Cortex-M register access / 寄存器访问 | Low-level / 底层,类似 CMSIS | -| cortex-m-rt | Runtime / startup code for Cortex-M | Replaces your startup.s | +| cortex-m-rt | Runtime/startup / 运行时/启动代码 | Replaces startup.s / 替代你的 startup.s | -| rtic | Real-Time Interrupt-driven Concurrency | Compile-time task scheduling, zero overhead | +| rtic | Real-Time Concurrency / 实时并发 | Compile-time scheduling / 编译时任务调度 | -| embassy | Async executor for embedded | async/await on bare metal | +| embassy | Async executor / 异步执行器 | async/await on bare metal / 裸机上的异步 | -| postcard | no_std serde serialization (binary) | Replaces serde_json when you can’t afford strings | +| postcard | Binary serialization / 二进制序列化 | Replaces serde_json / 替代 serde_json | -| thiserror | Derive macro for Error trait | Works in no_std since v2; prefer over anyhow | +| thiserror | Derive for Error / 为 Error trait 准备的派生宏 | Works in no_std / 在 no_std 中可用 | -| smoltcp | no_std TCP/IP stack | When you need networking without an OS | +| smoltcp | TCP/IP stack / TCP/IP 协议栈 | Networking without OS / 无需 OS 即可联网 |

  • A typical embedded C blinky:
  • 一个典型的嵌入式 C 语言 LED 闪烁程序:
// C — bare metal, vendor HAL
#include "stm32f4xx_hal.h"

void SysTick_Handler(void) {
    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
}

int main(void) {
    HAL_Init();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    GPIO_InitTypeDef gpio = { .Pin = GPIO_PIN_5, .Mode = GPIO_MODE_OUTPUT_PP };
    HAL_GPIO_Init(GPIOA, &gpio);
    HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / 1000);
    while (1) {}
}
  • The Rust equivalent (using embedded-hal + a board crate):
  • Rust 的等效实现(使用 embedded-hal + 开发板 crate):
#![no_std]
#![no_main]

use cortex_m_rt::entry;
- use panic_halt as _; // panic handler: infinite loop
+ use panic_halt as _; // panic handler / panic 处理器:死循环
use stm32f4xx_hal::{pac, prelude::*};

#[entry]
fn main() -> ! {
    let dp = pac::Peripherals::take().unwrap();
    let gpioa = dp.GPIOA.split();
    let mut led = gpioa.pa5.into_push_pull_output();

    let rcc = dp.RCC.constrain();
    let clocks = rcc.cfgr.freeze();
    let mut delay = dp.TIM2.delay_ms(&clocks);

    loop {
        led.toggle();
        delay.delay_ms(500u32);
    }
}
  • Key differences for C devs:
  • 给 C 开发者的关键区别说明:
    • Peripherals::take() returns Option — ensures the singleton pattern at compile time (no double-init bugs)
    • Peripherals::take() 返回 Option —— 确保在编译时满足单例模式(无重复初始化 bug)。
    • .split() moves ownership of individual pins — no risk of two modules driving the same pin
    • .split() 转移了单个引脚的所有权 —— 不存在两个模块驱动同一个引脚的风险。
    • All register access is type-checked — you can’t accidentally write to a read-only register
    • 所有寄存器访问都经过类型检查 —— 你不会意外地写入只读寄存器。
    • The borrow checker prevents data races between main and interrupt handlers (with RTIC)
    • 借用检查器防止了 main 与中断处理器之间的数据竞态(配合 RTIC)。
flowchart TD
-    A[Does your target have an OS?] -->|Yes| B[Use std]
+    A["Does your target have an OS? / 目标有操作系统吗?"] -->|Yes / 是| B["Use std / 使用 std"]
-    A -->|No| C[Do you have a heap allocator?]
+    A -->|No / 否| C["Do you have a heap allocator? / 有堆分配器吗?"]
-    C -->|Yes| D["Use #![no_std] + extern crate alloc"]
+    C -->|Yes / 有| D["Use #![no_std] + extern crate alloc / 使用 no_std + alloc"]
-    C -->|No| E["Use #![no_std] with core only"]
+    C -->|No / 没有| E["Use #![no_std] with core only / 仅使用 no_std + core"]
-    B --> F[Full Vec, HashMap, threads, fs, net]
+    B --> F["Full Vec, HashMap, threads, fs, net / 全套功能"]
-    D --> G[Vec, String, Box, BTreeMap — no fs/net/threads]
+    D --> G["Vec, String, Box, BTreeMap — no fs/net/threads / 无文件/网络/线程"]
-    E --> H[Fixed-size arrays, heapless collections, no allocation]
+    E --> H["Fixed-size arrays, heapless collections, no allocation / 固定大小数组、无分配"]
  • 🔴 Challenge — combines generics, MaybeUninit, and #[cfg(test)] in a no_std context
  • 🔴 Challenge / 挑战题 —— 在 no_std 环境中结合运用泛型、MaybeUninit#[cfg(test)]
  • In embedded systems you often need a fixed-size ring buffer (circular buffer) that
  • 在嵌入式系统中,你经常需要一个从不发生分配的固定大小环形缓冲区(Ring Buffer)。
  • never allocates. Implement one using only core (no alloc, no std).
  • 请仅使用 core(不使用 allocstd)实现一个。
  • Requirements:
  • 需求:
    • Generic over element type T: Copy
    • 对元素类型 T: Copy 泛型化
    • Fixed capacity N (const generic)
    • 固定容量 N(常量泛型)
    • push(&mut self, item: T) — overwrites oldest element when full
    • push(&mut self, item: T) —— 存满时覆盖最旧的元素
    • pop(&mut self) -> Option<T> — returns oldest element
    • pop(&mut self) -> Option<T> —— 返回最旧的元素
    • len(&self) -> usize
    • len(&self) -> usize
    • is_empty(&self) -> bool
    • is_empty(&self) -> bool
    • Must compile with #![no_std]
    • 必须能在 #![no_std] 下编译
#![allow(unused)]
fn main() {
- // Starter code
+ // Starter code / 入门代码
#![no_std]

use core::mem::MaybeUninit;

pub struct RingBuffer<T: Copy, const N: usize> {
-    buf: [MaybeUninit<T>; N],
+    buf: [MaybeUninit<T>; N], // 缓冲区
-    head: usize,  // next write position
+    head: usize,  // next write / 下一个写入位置
-    tail: usize,  // next read position
+    tail: usize,  // next read / 下一个读取位置
    count: usize,
}

impl<T: Copy, const N: usize> RingBuffer<T, N> {
    pub const fn new() -> Self {
        todo!()
    }
    pub fn push(&mut self, item: T) {
        todo!()
    }
    pub fn pop(&mut self) -> Option<T> {
        todo!()
    }
    pub fn len(&self) -> usize {
        todo!()
    }
    pub fn is_empty(&self) -> bool {
        todo!()
    }
}
}
-Solution +Solution / 解决方案
#![allow(unused)]
#![no_std]

fn main() {
use core::mem::MaybeUninit;

pub struct RingBuffer<T: Copy, const N: usize> {
    buf: [MaybeUninit<T>; N],
    head: usize,
    tail: usize,
    count: usize,
}

impl<T: Copy, const N: usize> RingBuffer<T, N> {
    pub const fn new() -> Self {
        Self {
-            // SAFETY: MaybeUninit does not require initialization
+            // SAFETY: MaybeUninit does not require / 安全说明:MaybeUninit 不需要初始化
            buf: unsafe { MaybeUninit::uninit().assume_init() },
            head: 0,
            tail: 0,
            count: 0,
        }
    }

    pub fn push(&mut self, item: T) {
        self.buf[self.head] = MaybeUninit::new(item);
        self.head = (self.head + 1) % N;
        if self.count == N {
-            // Buffer is full — overwrite oldest, advance tail
+            // Buffer full / 缓冲区已满 —— 覆盖最旧项,移动末端索引
            self.tail = (self.tail + 1) % N;
        } else {
            self.count += 1;
        }
    }

    pub fn pop(&mut self) -> Option<T> {
        if self.count == 0 {
            return None;
        }
-        // SAFETY: We only read positions that were previously written via push()
+        // SAFETY / 安全说明:我们只读取之前经由 push() 写入的位置
        let item = unsafe { self.buf[self.tail].assume_init() };
        self.tail = (self.tail + 1) % N;
        self.count -= 1;
        Some(item)
    }

    pub fn len(&self) -> usize {
        self.count
    }

    pub fn is_empty(&self) -> bool {
        self.count == 0
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn basic_push_pop() {
        let mut rb = RingBuffer::<u32, 4>::new();
        assert!(rb.is_empty());

        rb.push(10);
        rb.push(20);
        rb.push(30);
        assert_eq!(rb.len(), 3);

        assert_eq!(rb.pop(), Some(10));
        assert_eq!(rb.pop(), Some(20));
        assert_eq!(rb.pop(), Some(30));
        assert_eq!(rb.pop(), None);
    }

    #[test]
    fn overwrite_on_full() {
        let mut rb = RingBuffer::<u8, 3>::new();
        rb.push(1);
        rb.push(2);
        rb.push(3);
-        // Buffer full: [1, 2, 3]
+        // Buffer full / 缓冲区已满: [1, 2, 3]

-        rb.push(4); // Overwrites 1 → [4, 2, 3], tail advances
+        rb.push(4); // Overwrites 1 / 覆盖 1 → [4, 2, 3],末端索引前进
        assert_eq!(rb.len(), 3);
-        assert_eq!(rb.pop(), Some(2)); // oldest surviving
+        assert_eq!(rb.pop(), Some(2)); // oldest surviving / 现存最旧的
        assert_eq!(rb.pop(), Some(3));
        assert_eq!(rb.pop(), Some(4));
        assert_eq!(rb.pop(), None);
    }
}
}
  • Why this matters for embedded C devs:
  • 为什么这对嵌入式 C 开发者很重要:
    • MaybeUninit is Rust’s equivalent of uninitialized memory — the compiler
    • MaybeUninit 是 Rust 对“未初始化内存”的等价处理方式 —— 编译器
  • won’t insert zero-fills, just like char buf[N]; in C
  • 不会插入填零操作,就像 C 语言中的 char buf[N];
    • The unsafe blocks are minimal (2 lines) and each has a // SAFETY: comment
    • unsafe 块极少(仅 2 行),且每一处都有 // SAFETY: 注释。
    • The const fn new() means you can create ring buffers in static variables
    • const fn new() 意味着你可以直接在 static 变量中创建环形缓冲区,
  • without a runtime constructor
  • 而无需运行时构造函数。
    • The tests run on your host with cargo test even though the code is no_std
    • 即使代码是 no_std 的,测试也可以通过 cargo test 在你的主机上运行。

Embedded Deep Dive / 嵌入式深入解析

MMIO and Volatile Register Access / MMIO 与 Volatile 寄存器访问

What you’ll learn / 你将学到: Type-safe hardware register access in embedded Rust — volatile MMIO patterns, register abstraction crates, and how Rust’s type system can encode register permissions that C’s volatile keyword cannot.

嵌入式 Rust 中的类型安全硬件寄存器访问 —— volatile MMIO 模式、寄存器抽象 crate,以及 Rust 的类型系统如何编码 C 语言中的 volatile 关键字无法表示的寄存器权限。

  • In C firmware, you access hardware registers via volatile pointers to specific
    • 在 C 语言固件中,你通过指向特定内存地址的 volatile 指针来访问硬件寄存器。
  • memory addresses. Rust has equivalent mechanisms — but with type safety.
  • Rust 具有等效的机制 —— 但具备类型安全性。
// C — typical MMIO register access
+// C — 典型的 MMIO 寄存器访问
#define GPIO_BASE     0x40020000
#define GPIO_MODER    (*(volatile uint32_t*)(GPIO_BASE + 0x00))
#define GPIO_ODR      (*(volatile uint32_t*)(GPIO_BASE + 0x14))

void toggle_led(void) {
-    GPIO_ODR ^= (1 << 5);  // Toggle pin 5
+    GPIO_ODR ^= (1 << 5);  // Toggle pin 5 / 翻转引脚 5
}
#![allow(unused)]
fn main() {
// Rust — raw volatile (low-level, rarely used directly)
+// Rust — 原始 volatile(底层,极少直接使用)
use core::ptr;

const GPIO_BASE: usize = 0x4002_0000;
const GPIO_ODR: *mut u32 = (GPIO_BASE + 0x14) as *mut u32;

/// # Safety
- /// Caller must ensure GPIO_BASE is a valid mapped peripheral address.
+ /// Caller must ensure / 调用者必须确保 GPIO_BASE 是有效的映射外设地址。
unsafe fn toggle_led() {
-    // SAFETY: GPIO_ODR is a valid memory-mapped register address.
+    // SAFETY / 安全说明:GPIO_ODR 是有效的内存映射寄存器地址。
    let current = unsafe { ptr::read_volatile(GPIO_ODR) };
-    unsafe { ptr::write_volatile(GPIO_ODR, current ^ (1 << 5)) };
+    unsafe { ptr::write_volatile(GPIO_ODR, current ^ (1 << 5)) }; // 翻转位 5
}
}
  • In practice, you never write raw volatile pointers. Instead, svd2rust generates
  • 在实践中,你绝不会手动编写原始 volatile 指针。相反,svd2rust 会根据芯片的 SVD 文件(与 IDE 调试视图中使用的 XML 文件相同)生成 外设访问 Crate (PAC)
  • a Peripheral Access Crate (PAC) from the chip’s SVD file (the same XML file used by
  • your IDE’s debug view):
#![allow(unused)]
fn main() {
- // Generated PAC code (you don't write this — svd2rust does)
+ // Generated PAC code / 生成的 PAC 代码(不是你写的,是 svd2rust 生成的)
- // The PAC makes invalid register access a compile error
+ // PAC 将无效的寄存器访问变为编译错误

- // Usage with PAC:
+ // Usage with PAC / 配合 PAC 使用:
use stm32f4::stm32f401;  // PAC crate for your chip / 芯片对应的 PAC crate

fn configure_gpio(dp: stm32f401::Peripherals) {
-    // Enable GPIOA clock — type-safe, no magic numbers
+    // Enable GPIOA clock / 启用 GPIOA 时钟 —— 类型安全,无魔法数字
    dp.RCC.ahb1enr.modify(|_, w| w.gpioaen().enabled());

-    // Set pin 5 to output — can't accidentally write to a read-only field
+    // Set pin 5 to output / 将引脚 5 设为输出 —— 不会意外写入只读字段
    dp.GPIOA.moder.modify(|_, w| w.moder5().output());

-    // Toggle pin 5 — type-checked field access
+    // Toggle pin 5 / 翻转引脚 5 —— 类型检查过的字段访问
    dp.GPIOA.odr.modify(|r, w| {
-        // SAFETY: toggling a single bit in a valid register field.
+        // SAFETY / 安全说明:在有效寄存器字段中翻转单个位。
        unsafe { w.bits(r.bits() ^ (1 << 5)) }
    });
}
}

-| C register access | Rust PAC equivalent | +| C register access / C 寄存器访问 | Rust PAC equivalent / Rust PAC 等价物 | |—————––|———————| -| #define REG (*(volatile uint32_t*)ADDR) | PAC crate generated by svd2rust | -| #define REG (*(volatile uint32_t*)ADDR) | 由 svd2rust 生成的 PAC crate | -| REG |= BITMASK; | periph.reg.modify(\|_, w\| w.field().variant()) | -| REG |= BITMASK; | periph.reg.modify(...) | -| value = REG; | let val = periph.reg.read().field().bits() | -| value = REG; | let val = periph.reg.read()... | -| Wrong register field → silent UB | Compile error — field doesn’t exist | +| Wrong field / 错误字段 → silent UB | Compile error / 编译错误 —— 字段不存在 | -| Wrong register width → silent UB | Type-checked — u8 vs u16 vs u32 | +| Wrong width / 错误宽度 → silent UB | Type-checked / 类型检查 —— u8 vs u16 vs u32 |

  • C firmware uses __disable_irq() / __enable_irq() and ISR functions with void
  • C 语言固件使用 __disable_irq() / __enable_irq() 以及签名为 void 的 ISR 函数。
  • signatures. Rust provides type-safe equivalents.
  • Rust 提供了类型安全的等价方案。
// C — traditional interrupt handler
+// C — 传统中断处理器
volatile uint32_t tick_count = 0;

- void SysTick_Handler(void) {   // Naming convention is critical — get it wrong → HardFault
+ void SysTick_Handler(void) {   // Naming critical / 命名规范至关重要 —— 弄错会导致 HardFault
    tick_count++;
}

uint32_t get_ticks(void) {
    __disable_irq();
-    uint32_t t = tick_count;   // Read inside critical section
+    uint32_t t = tick_count;   // Critical section / 在临界区内读取
    __enable_irq();
    return t;
}
#![allow(unused)]
fn main() {
// Rust — using cortex-m and critical sections
+// Rust — 使用 cortex-m 和临界区
use core::cell::Cell;
use cortex_m::interrupt::{self, Mutex};

- // Shared state protected by a critical-section Mutex
+ // Shared state / 由由临界区 Mutex 保护的共享状态
static TICK_COUNT: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));

- #[cortex_m_rt::exception]     // Attribute ensures correct vector table placement
+ #[cortex_m_rt::exception]     // Ensures vector table / 属性确保正确放置在向量表中
- fn SysTick() {                // Compile error if name doesn't match a valid exception
+ fn SysTick() {                // Error if name mismatch / 如果名称与有效异常不匹配,则报错
-    interrupt::free(|cs| {    // cs = critical section token (proof IRQs disabled)
+    interrupt::free(|cs| {    // cs = token / cs = 临界区令牌(证明 IRQ 已禁用)
        let count = TICK_COUNT.borrow(cs).get();
        TICK_COUNT.borrow(cs).set(count + 1);
    });
}

fn get_ticks() -> u32 {
    interrupt::free(|cs| TICK_COUNT.borrow(cs).get())
}
}
  • For complex firmware with multiple interrupt priorities, RTIC (formerly RTFM) provides
  • 对于具有多个中断优先级的复杂固件,RTIC(原名 RTFM)提供
  • compile-time task scheduling with zero overhead:
  • 零开销的编译时任务调度
#![allow(unused)]
fn main() {
#[rtic::app(device = stm32f4xx_hal::pac, dispatchers = [USART1])]
mod app {
    use stm32f4xx_hal::prelude::*;

    #[shared]
    struct Shared {
-        temperature: f32,   // Shared between tasks — RTIC manages locking
+        temperature: f32,   // Shared / 任务间共享 —— RTIC 管理加锁
    }

    #[local]
    struct Local {
        led: stm32f4xx_hal::gpio::Pin<'A', 5, stm32f4xx_hal::gpio::Output>,
    }

    #[init]
    fn init(cx: init::Context) -> (Shared, Local) {
        let dp = cx.device;
        let gpioa = dp.GPIOA.split();
        let led = gpioa.pa5.into_push_pull_output();
        (Shared { temperature: 25.0 }, Local { led })
    }

-    // Hardware task: runs on SysTick interrupt
+    // Hardware task / 硬件任务:在 SysTick 中断上运行
    #[task(binds = SysTick, shared = [temperature], local = [led])]
    fn tick(mut cx: tick::Context) {
        cx.local.led.toggle();
        cx.shared.temperature.lock(|temp| {
-            // RTIC guarantees exclusive access here — no manual locking needed
+            // Exclusive access / RTIC 保证此处的排他性访问 —— 无需手动加锁
            *temp += 0.1;
        });
    }
}
}
  • Why RTIC matters for C firmware devs:
  • 为什么 RTIC 对 C 语言固件开发者很重要:
    • The #[shared] annotation replaces manual mutex management
    • #[shared] 标注取代了手动的互斥锁管理。
    • Priority-based preemption is configured at compile time — no runtime overhead
    • 基于优先级的抢占是在编译时配置的 —— 无运行时开销。
    • Deadlock-free by construction (the framework proves it at compile time)
    • 结构上无死锁(框架在编译时证明了这一点)。
    • ISR naming errors are compile errors, not runtime HardFaults
    • ISR 命名错误会变为编译错误,而不是运行时的 HardFault。
  • In C, when something goes wrong in firmware, you typically reset or blink an LED.
  • 在 C 语言仪表中,当固件出错时,你通常会复位或闪烁 LED。
  • Rust’s panic handler gives you structured control:
  • Rust 的 panic 处理器为你提供了结构化的控制:
#![allow(unused)]
fn main() {
- // Strategy 1: Halt (for debugging — attach debugger, inspect state)
+ // Strategy 1: Halt / 策略 1:停机(用于调试 —— 连接调试器,检查状态)
use panic_halt as _;  // Infinite loop on panic

- // Strategy 2: Reset the MCU
+ // Strategy 2: Reset / 策略 2:复位 MCU
use panic_reset as _;  // Triggers system reset

- // Strategy 3: Log via probe (development)
+ // Strategy 3: Log via probe / 策略 3:通过探针记录日志(开发阶段)
use panic_probe as _;  // Sends panic info over debug probe (with defmt)

- // Strategy 4: Log over defmt then halt
+ // Strategy 4: Log then halt / 策略 4:通过 defmt 记录日志后停机
use defmt_panic as _;  // Rich panic messages over ITM/RTT

- // Strategy 5: Custom handler (production firmware)
+ // Strategy 5: Custom handler / 策略 5:自定义处理器(生产环境固件)
use core::panic::PanicInfo;

#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
-    // 1. Disable interrupts to prevent further damage
+    // 1. Disable IRQs / 禁用中断以防止进一步损坏
    cortex_m::interrupt::disable();

-    // 2. Write panic info to a reserved RAM region (survives reset)
-    // SAFETY: PANIC_LOG is a reserved memory region defined in linker script.
+    // 2. Write info / 将 panic 信息写入预留的 RAM 区域(复位后依然存在)
+    // SAFETY / 安全说明:PANIC_LOG 是链接脚本中定义的预留内存区域。
    unsafe {
        let log = 0x2000_0000 as *mut [u8; 256];
-        // Write truncated panic message
+        // Write message / 写入截断的 panic 消息
        use core::fmt::Write;
        let mut writer = FixedWriter::new(&mut *log);
        let _ = write!(writer, "{}", info);
    }

-    // 3. Trigger watchdog reset (or blink error LED)
+    // 3. Reset or Blink / 触发看门狗复位(或闪烁错误 LED)
    loop {
-        cortex_m::asm::wfi();  // Wait for interrupt (low power while halted)
+        cortex_m::asm::wfi();  // Wait for interrupt / 等待中断(停机时降低功耗)
    }
}
}
  • C firmware devs write linker scripts to define FLASH/RAM regions. Rust embedded
  • C 语言固件开发者编写链接脚本来定义 FLASH/RAM 区域。
  • uses the same concept via memory.x:
  • 嵌入式 Rust 通过 memory.x 使用相同的概念:
- /* memory.x — placed at crate root, consumed by cortex-m-rt */
+ /* memory.x — placed at root / 放置在 crate 根目录,由 cortex-m-rt 使用 */
MEMORY
{
-   /* Adjust for your MCU — these are STM32F401 values */
+   /* STM32F401 values / 针对你的 MCU 进行调整 —— 这些是 STM32F401 的值 */
  FLASH : ORIGIN = 0x08000000, LENGTH = 512K
  RAM   : ORIGIN = 0x20000000, LENGTH = 96K
}

- /* Optional: reserve space for panic log (see panic handler above) */
+ /* Optional: panic log / 可选:为 panic 日志预留空间(见上文) */
_panic_log_start = ORIGIN(RAM);
_panic_log_size  = 256;
- # .cargo/config.toml — set the target and linker flags
+ # .cargo/config.toml — 设置目标和链接标志
[target.thumbv7em-none-eabihf]
- runner = "probe-rs run --chip STM32F401RE"  # flash and run via debug probe
+ runner = "probe-rs run --chip STM32F401RE"  # 使用调试探针烧录并运行
rustflags = [
-     "-C", "link-arg=-Tlink.x",              # cortex-m-rt linker script
+     "-C", "link-arg=-Tlink.x",              # cortex-m-rt 的链接脚本
]

[build]
- target = "thumbv7em-none-eabihf"            # Cortex-M4F with hardware FPU
+ target = "thumbv7em-none-eabihf"            # 带有硬件 FPU 的 Cortex-M4F

-| C linker script | Rust equivalent | +| C linker script / C 链接脚本 | Rust equivalent / Rust 等价物 | |—————–|—————–| -| MEMORY { FLASH ..., RAM ... } | memory.x at crate root | -| MEMORY { FLASH ..., RAM ... } | crate 根目录下的 memory.x | -| __attribute__((section(".data"))) | #[link_section = ".data"] | -| __attribute__((section(".data"))) | #[link_section = ".data"] | -| -T linker.ld in Makefile | -C link-arg=-Tlink.x in .cargo/config.toml | -| -T linker.ld in Makefile | .cargo/config.toml 中的链接标志 | -| Startup assembly (startup.s) | cortex-m-rt #[entry] macro | +| Startup (startup.s) | cortex-m-rt #[entry] macro / 宏 |

  • The embedded-hal crate defines traits for SPI, I2C, GPIO, UART, etc. Drivers
  • embedded-hal crate 定义了 SPI、I2C、GPIO、UART 等 trait。
  • written against these traits work on any MCU — this is Rust’s killer feature
  • 基于这些 trait 编写的驱动程序可以在任何 MCU 上运行 —— 这是 Rust 在嵌入式领域复用性方面的“杀手级特性”。
// C — driver tightly coupled to STM32 HAL
+// C — 驱动程序与 STM32 HAL 紧耦合
#include "stm32f4xx_hal.h"

float read_temperature(I2C_HandleTypeDef* hi2c, uint8_t addr) {
    uint8_t buf[2];
    HAL_I2C_Mem_Read(hi2c, addr << 1, 0x00, I2C_MEMADD_SIZE_8BIT,
                     buf, 2, HAL_MAX_DELAY);
    int16_t raw = ((int16_t)buf[0] << 4) | (buf[1] >> 4);
    return raw * 0.0625;
}
- // Problem: This driver ONLY works with STM32 HAL. Porting to Nordic = rewrite.
+ // Problem / 问题:驱动仅适用于 STM32 HAL。移植到 Nordic = 重写。
#![allow(unused)]
fn main() {
// Rust — driver works on ANY MCU that implements embedded-hal
+// Rust — 驱动程序适用于任何实现 embedded-hal 的 MCU
use embedded_hal::i2c::I2c;

pub struct Tmp102<I2C> {
    i2c: I2C,
    address: u8,
}

impl<I2C: I2c> Tmp102<I2C> {
    pub fn new(i2c: I2C, address: u8) -> Self {
        Self { i2c, address }
    }

    pub fn read_temperature(&mut self) -> Result<f32, I2C::Error> {
        let mut buf = [0u8; 2];
        self.i2c.write_read(self.address, &[0x00], &mut buf)?;
        let raw = ((buf[0] as i16) << 4) | ((buf[1] as i16) >> 4);
        Ok(raw as f32 * 0.0625)
    }
}

- // Works on STM32, Nordic nRF, ESP32, RP2040 — any chip with an embedded-hal I2C impl
+ // Works on STM32, Nordic, ESP32, RP2040 — any MCU / 适用于 STM32、nRF、ESP32、RP2040 以及任何具有 I2C 实现的芯片
}
graph TD
-    subgraph "C Driver Architecture"
+    subgraph "C Driver Architecture / C 驱动架构"
-        CD["Temperature Driver"]
+        CD["Temperature Driver / 温度驱动"]
-        CD --> STM["STM32 HAL"]
+        CD --> STM["STM32 HAL"]
-        CD -.->|"Port = REWRITE"| NRF["Nordic HAL"]
+        CD -.->|"Port = REWRITE / 移植 = 重写"| NRF["Nordic HAL"]
-        CD -.->|"Port = REWRITE"| ESP["ESP-IDF"]
+        CD -.->|"Port = REWRITE / 移植 = 重写"| ESP["ESP-IDF"]
     end
     
-    subgraph "Rust embedded-hal Architecture"
+    subgraph "Rust embedded-hal Architecture / Rust embedded-hal 架构"
-        RD["Temperature Driver<br/>impl&lt;I2C: I2c&gt;"]
+        RD["Temperature Driver / 温度驱动<br/>impl&lt;I2C: I2c&gt;"]
-        RD --> EHAL["embedded-hal::I2c trait"]
+        RD --> EHAL["embedded-hal::I2c trait"]
-        EHAL --> STM2["stm32f4xx-hal"]
+        EHAL --> STM2["stm32f4xx-hal"]
-        EHAL --> NRF2["nrf52-hal"]
+        EHAL --> NRF2["nrf52-hal"]
-        EHAL --> ESP2["esp-hal"]
+        EHAL --> ESP2["esp-hal"]
-        EHAL --> RP2["rp2040-hal"]
+        EHAL --> RP2["rp2040-hal"]
-        NOTE["Write driver ONCE,<br/>runs on ALL chips"]
+        NOTE["Write ONCE / 一次编写<br/>runs on ALL / 运行在所有芯片上"]
     end
     
     style CD fill:#ffa07a,color:#000
@@ -194,11 +195,11 @@
     style NOTE fill:#91e5a3,color:#000
  • The alloc crate gives you Vec, String, Box — but you need to tell Rust
  • alloc crate 为你提供了 VecStringBox —— 但你需要告诉 Rust
  • where heap memory comes from. This is the equivalent of implementing malloc()
  • 堆内存从何处而来。这相当于为你的平台实现 malloc()
  • for your platform:
#![allow(unused)]
#![no_std]
fn main() {
@@ -211,21 +212,21 @@
    // Initialize the allocator with a memory region
    // (typically a portion of RAM not used by stack or static data)
+    // 初始化带有内存区域的分配器(通常是栈和静态数据之外的 RAM 区域)
    {
        const HEAP_SIZE: usize = 4096;
        static mut HEAP_MEM: [u8; HEAP_SIZE] = [0; HEAP_SIZE];
-        // SAFETY: HEAP_MEM is only accessed here during init, before any allocation.
+        // SAFETY / 安全说明:HEAP_MEM 仅在分配开始前的初始化期间在此处访问。
        unsafe { HEAP.init(HEAP_MEM.as_ptr() as usize, HEAP_SIZE) }
    }

-    // Now you can use heap types!
+    // Now use heap types / 现在可以使用堆类型了!
    let mut log_buffer: Vec<u8> = Vec::with_capacity(256);
    let name: String = String::from("sensor_01");
-    // ...
+    // // ...

    loop {}
}
}

-| C heap setup | Rust equivalent | +| C heap setup / C 堆设置 | Rust equivalent / Rust 等价物 | |———––|—————–| -| _sbrk() / custom malloc() | #[global_allocator] + Heap::init() | -| _sbrk() / custom malloc() | #[global_allocator] + Heap::init() | -| configTOTAL_HEAP_SIZE (FreeRTOS) | HEAP_SIZE constant | -| configTOTAL_HEAP_SIZE (FreeRTOS) | HEAP_SIZE 常量 | -| pvPortMalloc() | alloc::vec::Vec::new() — automatic | -| pvPortMalloc() | 自动完成 | -| Heap exhaustion → undefined behavior | alloc_error_handler → controlled panic | +| Exhaustion / 堆耗尽 → UB | alloc_error_handler → controlled panic |

  • Real projects (like a large Rust workspace) often have:
  • 实际项目(如大型 Rust 工作区)通常包含:
    • no_std library crates for hardware-portable logic
    • 包含硬件可移植逻辑的 no_std 库 crate
    • std binary crates for the Linux application layer
    • 针对 Linux 应用层的 std 二进制 crate
workspace_root/
@@ -242,12 +243,12 @@
    └── src/main.rs         # Uses std::fs, std::net, etc.
  • The key pattern: the protocol crate uses #![no_std] so it compiles for both
  • 关键点在于:protocol crate 使用了 #![no_std],因此它可以同时
  • the MCU firmware and the Linux host tool. Shared code, zero duplication.
  • MCU 固件和 Linux 主机工具编译。代码共享,零冗余。
- # protocol/Cargo.toml
+ # protocol/Cargo.toml / 协议 crate 的配置
[package]
name = "protocol"

@@ -255,11 +256,11 @@
default = []
std = []  # Optional: enable std-specific features when building for host

[dependencies]
serde = { version = "1", default-features = false, features = ["derive"] }
- # Note: default-features = false drops serde's std dependency
+ # Note / 注意:default-features = false 删除了 serde 对 std 的依赖
#![allow(unused)]
fn main() {
// protocol/src/lib.rs
#![cfg_attr(not(feature = "std"), no_std)]
@@ -274,48 +275,48 @@
    pub value: i32,
    pub fault_code: u16,
}

- // This function works in both no_std and std contexts
+ // Works in both / 此函数同时适用于 no_std 和 std 环境
pub fn parse_packet(data: &[u8]) -> Result<DiagPacket, &'static str> {
    if data.len() < 8 {
        return Err("packet too short");
    }
    Ok(DiagPacket {
        sensor_id: u16::from_le_bytes([data[0], data[1]]),
        value: i32::from_le_bytes([data[2], data[3], data[4], data[5]]),
        fault_code: u16::from_le_bytes([data[6], data[7]]),
    })
}
}
  • Write a no_std driver for a hypothetical LED controller that communicates over SPI.
  • 为一个通过 SPI 通信的虚拟 LED 控制器编写 no_std 驱动程序。
  • The driver should be generic over any SPI implementation using embedded-hal.
  • 驱动程序应对使用 embedded-hal 的任何 SPI 实现泛型化。
  • Requirements:
  • 需求:
    1. Define a LedController<SPI> struct
    1. 定义 LedController<SPI> 结构体
    1. Implement new(), set_brightness(led: u8, brightness: u8), and all_off()
    1. 实现 new()set_brightness(led: u8, brightness: u8)all_off()
    1. SPI protocol: send [led_index, brightness_value] as 2-byte transaction
    1. SPI 协议:将 [led_index, brightness_value] 作为 2 字节事务发送
    1. Write tests using a mock SPI implementation
    1. 使用 Mock SPI 实现编写测试
#![allow(unused)]
fn main() {
- // Starter code
+ // Starter code / 入门代码
#![no_std]
use embedded_hal::spi::SpiDevice;

pub struct LedController<SPI> {
    spi: SPI,
    num_leds: u8,
}

- // TODO: Implement new(), set_brightness(), all_off()
+ // TODO: Implement / 待办:实现相关方法
- // TODO: Create MockSpi for testing
+ // TODO: Create MockSpi / 待办:创建测试用的 MockSpi
}
  • Solution (click to expand)
  • Solution (click to expand) / 解决方案(点击展开)
#![allow(unused)]
#![no_std]
fn main() {
use embedded_hal::spi::SpiDevice;
@@ -324,11 +325,11 @@
    }

    pub fn set_brightness(&mut self, led: u8, brightness: u8) -> Result<(), SPI::Error> {
        if led >= self.num_leds {
-            return Ok(()); // Silently ignore out-of-range LEDs
+            return Ok(()); // Ignore out-of-range / 静默忽略越界引脚
        }
        self.spi.write(&[led, brightness])
    }

    pub fn all_off(&mut self) -> Result<(), SPI::Error> {
@@ -342,11 +343,11 @@
mod tests {
    use super::*;

-    // Mock SPI that records all transactions
+    // Mock SPI / 记录所有事务的 Mock SPI
    struct MockSpi {
        transactions: Vec<Vec<u8>>,
    }

-    // Minimal error type for mock
+    // Minimal error / Mock 使用的极简错误类型
    #[derive(Debug)]
    struct MockError;
    impl embedded_hal::spi::Error for MockError {
@@ -402,11 +403,11 @@

</details>

- ## Debugging Embedded Rust — probe-rs, defmt, and VS Code
+ ## Debugging Embedded Rust — probe-rs, defmt, and VS Code / 嵌入式 Rust 调试 —— probe-rs、defmt 与 VS Code

- C firmware developers typically debug with OpenOCD + GDB or vendor-specific IDEs
+ C 固件开发者通常使用 OpenOCD + GDB 或厂商特定的 IDE(Keil、IAR、Segger Ozone)进行调试。
- (Keil, IAR, Segger Ozone). Rust's embedded ecosystem has converged on **probe-rs**
+ Rust 嵌入式生态系统已经趋向于使用 **probe-rs**
- as the unified debug probe interface, replacing the OpenOCD + GDB stack with a
+ 作为统一的调试探针接口,用一个 Rust 原生工具取代了 OpenOCD + GDB 栈。
- single, Rust-native tool.

- ### probe-rs — The All-in-One Debug Probe Tool
+ ### probe-rs — The All-in-One Debug Probe Tool / probe-rs —— 全能调试探针工具

- `probe-rs` replaces the OpenOCD + GDB combination. It supports CMSIS-DAP,
+ `probe-rs` 取代了 OpenOCD + GDB 的组合。它开箱即用地支持 CMSIS-DAP、
- ST-Link, J-Link, and other debug probes out of the box:
+ ST-Link、J-Link 等调试探针:

```bash
- # Install probe-rs (includes cargo-flash and cargo-embed)
+ # Install probe-rs / 安装 probe-rs (包含 cargo-flash 和 cargo-embed)
cargo install probe-rs-tools

- # Flash and run your firmware
+ # Flash and run / 烧录并运行固件
cargo flash --chip STM32F401RE --release

- # Flash, run, and open RTT (Real-Time Transfer) console
+ # Flash and RTT / 烧录、运行并打开 RTT(实时传输)控制台
cargo embed --chip STM32F401RE
}
  • probe-rs vs OpenOCD + GDB:
  • probe-rs vs OpenOCD + GDB 对比

-| Aspect | OpenOCD + GDB | probe-rs | +| Aspect / 维度 | OpenOCD + GDB | probe-rs | |––––|–––––––|–––––| -| Install | 2 separate packages + scripts | cargo install probe-rs-tools | +| Install / 安装 | 2 separate pkgs / 两个独立包 + 脚本 | cargo install | -| Config | .cfg files per board/probe | --chip flag or Embed.toml | +| Config / 配置 | 针对开发板/探针的 .cfg 文件 | --chipEmbed.toml | -| Console output | Semihosting (very slow) | RTT (~10× faster) | +| Console / 控制台 | Semihosting (极慢) | RTT (约快 10 倍) | -| Log framework | printf | defmt (structured, zero-cost) | +| Log / 日志 | printf | defmt (结构化、零成本) | -| Flash algorithm | XML pack files | Built-in for 1000+ chips | -| Flash / 烧录 | XML pack 文件 | 内置支持 1000+ 芯片 | -| GDB support | Native | probe-rs gdb adapter | +| GDB support / 支持 | Native / 原生 | probe-rs gdb 适配器 |

  • Instead of juggling .cfg and .gdbinit files, probe-rs uses a single config:
  • probe-rs 使用单一配置,省去了在 .cfg.gdbinit 文件之间周旋的烦恼:
- # Embed.toml — placed in your project root
+ # Embed.toml — 放置在项目根目录
[default.general]
chip = "STM32F401RETx"

[default.rtt]
- enabled = true           # Enable Real-Time Transfer console
+ enabled = true           # 启用 RTT 控制台
channels = [
    { up = 0, mode = "BlockIfFull", name = "Terminal" },
]

[default.flashing]
- enabled = true           # Flash before running
+ enabled = true           # 运行前进行烧录
restore_unwritten_bytes = false

[default.reset]
- halt_afterwards = false  # Start running after flash + reset
+ halt_afterwards = false  # 烧录并复位后开始运行

[default.gdb]
- enabled = false          # Set true to expose GDB server on :1337
+ enabled = false          # 设为 true 以在 1337 端口暴露 GDB 服务
gdb_connection_string = "127.0.0.1:1337"
- # With Embed.toml, just run:
+ # With Embed.toml / 有了配置后只需运行:
- cargo embed              # Flash + RTT console — zero flags needed
+ cargo embed              # 烧录 + RTT 控制台 —— 无需额外参数
- cargo embed --release    # Release build
+ cargo embed --release    # Release 构建
  • defmt (deferred formatting) replaces printf debugging. Format strings are
  • defmt 取代了 printf 调试。格式化字符串存储在 ELF 文件中,而不是
  • stored in the ELF file, not in flash — so log calls on the target send only
  • FLASH 中 —— 因此目标机上的日志调用仅发送索引和参数字节。
  • an index + argument bytes. This makes logging 10–100× faster than printf
  • 这使得日志记录比 printf 快 10–100 倍,且仅消耗极小一部分 FLASH 空间:
  • and uses a fraction of the flash space:
#![no_std]
#![no_main]

use defmt::{info, warn, error, debug, trace};
- use defmt_rtt as _; // RTT transport — links the defmt output to probe-rs
+ use defmt_rtt as _; // RTT transport / RTT 传输 —— 将 defmt 输出链接到 probe-rs

#[cortex_m_rt::entry]
fn main() -> ! {
    info!("Boot complete, firmware v{}", env!("CARGO_PKG_VERSION"));

    let sensor_id: u16 = 0x4A;
    let temperature: f32 = 23.5;

-    // Format strings stay in ELF, not flash — near-zero overhead
+    // Minimal overhead / 格式化字符串留在 ELF 中,不在 FLASH 中 —— 近乎零开销
    debug!("Sensor {:#06X}: {:.1}°C", sensor_id, temperature);

    if temperature > 80.0 {
        warn!("Overtemp on sensor {:#06X}: {:.1}°C", sensor_id, temperature);
    }

    loop {
-        cortex_m::asm::wfi(); // Wait for interrupt
+        cortex_m::asm::wfi(); // Wait for IRQ / 等待中断
    }
}

- // Custom types — derive defmt::Format instead of Debug
+ // Custom types / 自定义类型 —— 派生 defmt::Format 替代 Debug
#[derive(defmt::Format)]
struct SensorReading {
    id: u16,
    value: i32,
    status: SensorStatus,
}

#[derive(defmt::Format)]
enum SensorStatus {
    Ok,
    Warning,
    Fault(u8),
}

- // Usage:
- // info!("Reading: {:?}", reading);  // <-- uses defmt::Format, NOT std Debug
+ // info!("Reading: {:?}", reading);  // <-- 使用 Format trait,而非 std Debug
  • defmt vs printf vs log:
  • defmt vs printf vs log 对比

-| Feature | C printf (semihosting) | Rust log crate | defmt | +| Feature / 特性 | C printf | Rust log | defmt | |———|———————––|—————––|———| -| Speed | ~100ms per call | N/A (needs std) | ~1μs per call | +| Speed / 速度 | ~100ms / call | N/A (needs std) | ~1μs / call | -| Flash usage | Full format strings | Full format strings | Index only (bytes) | -| Flash / 占用 | 全量格式化字符串 | 全量字符串 | 仅索引 (字节) | -| Transport | Semihosting (halts CPU) | Serial/UART | RTT (non-blocking) | -| Transport / 传输 | Semihosting (停掉 CPU) | 串口/UART | RTT (无阻塞) | -| Structured output | No | Text only | Typed, binary-encoded | -| 结构化输出 | ❌ 否 | 仅文本 | ✅ 有类型、二进制编码 | -| no_std | Via semihosting | Facade only (backends need std) | ✅ Native | +| no_std | Semihosting / 模拟 | Facade only / 门面 | ✅ Native / 原生 | -| Filter levels | Manual #ifdef | RUST_LOG=debug | defmt::println + features | +| Filter / 过滤 | 手动 #ifdef | 环境变量 | 通过特性开关 |

  • With the probe-rs VS Code extension, you get full graphical debugging —
  • 通过 probe-rs 的 VS Code 扩展,你可以获得全图形化调试 ——
  • breakpoints, variable inspection, call stack, and register view:
  • 断点、变量检查、调用栈以及寄存器视图:
- // .vscode/launch.json
+ // .vscode/launch.json / VS Code 启动配置
{
    "version": "0.2.0",
    "configurations": [
@@ -482,12 +483,12 @@
    ]
}
  • Install the extension:
  • 安装扩展:
#![allow(unused)]
fn main() {
ext install probe-rs.probe-rs-debugger
}
graph LR
-    subgraph "C Workflow (Traditional)"
+    subgraph "C Workflow (Traditional) / C 传统工作流"
-        C1["Write code"] --> C2["make flash"]
+        C1["Write / 编写代码"] --> C2["make flash / 烧录"]
-        C2 --> C3["openocd -f board.cfg"]
+        C2 --> C3["openocd -f board.cfg"]
-        C3 --> C4["arm-none-eabi-gdb<br/>target remote :3333"]
+        C3 --> C4["gdb<br/>target remote :3333"]
-        C4 --> C5["printf via semihosting<br/>(~100ms per call, halts CPU)"]
+        C4 --> C5["printf via semihosting<br/>(~100ms/call, 停掉 CPU)"]
     end
     
-    subgraph "Rust Workflow (probe-rs)"
+    subgraph "Rust Workflow (probe-rs) / Rust 工作流"
-        R1["Write code"] --> R2["cargo embed"]
+        R1["Write / 编写代码"] --> R2["cargo embed / 一键操作"]
-        R2 --> R3["Flash + RTT console<br/>in one command"]
+        R2 --> R3["Flash + RTT console / 烧录 + RTT 控制台"]
-        R3 --> R4["defmt logs stream<br/>in real-time (~1μs)"]
+        R4["defmt logs / 实时日志 (~1μs)"]
-        R2 -.->|"Or"| R5["VS Code F5<br/>Full GUI debugger"]
+        R3 --> R4
+        R2 -.->|"Or / 或"| R5["VS Code F5 / 图形化调试"]
     end
     
     style C5 fill:#ffa07a,color:#000
@@ -496,25 +497,22 @@
     style R5 fill:#91e5a3,color:#000

-| C Debug Action | Rust Equivalent | +| C Debug Action / C 调试动作 | Rust Equivalent / Rust 等价物 | |—————|—————–| -| openocd -f board/st_nucleo_f4.cfg | probe-rs info (auto-detects probe + chip) | +| openocd ... | probe-rs info (auto-detect / 自动检测) | -| arm-none-eabi-gdb -x .gdbinit | probe-rs gdb --chip STM32F401RE | +| gdb -x .gdbinit | probe-rs gdb --chip ... | -| target remote :3333 | GDB connects to localhost:1337 | -| target remote :3333 | GDB 连接至 localhost:1337 | -| monitor reset halt | probe-rs reset --chip ... | -| monitor reset halt | probe-rs reset ... | -| load firmware.elf | cargo flash --chip ... | -| load firmware.elf | cargo flash ... | -| printf("debug: %d\n", val) (semihosting) | defmt::info!("debug: {}", val) (RTT) | -| Keil/IAR GUI debugger | VS Code + probe-rs-debugger extension | -| Keil/IAR GUI | VS Code + probe-rs 扩展 | -| Segger SystemView | defmt + probe-rs RTT viewer | -| Segger SystemView | defmt + probe-rs RTT 查看器 |

  • Cross-reference: For advanced unsafe patterns used in embedded drivers

  • 交叉引用:有关嵌入式驱动中应用的高级 unsafe 模式(引脚映射、自定义 arena/slab 分配器),请参阅配套的《Rust 设计模式》指南中的“引脚映射 —— 结构化固定(Pin Projections — Structural Pinning)”和“自定义分配器 —— Arena 与 Slab 模式”章节。

  • (pin projections, custom arena/slab allocators), see the companion

  • Rust Patterns guide, sections “Pin Projections — Structural Pinning”

  • and “Custom Allocators — Arena and Slab Patterns.”

Case Study Overview: C++ to Rust Translation / 案例研究概览:C++ 到 Rust 的迁移

What you’ll learn / 你将学到: Lessons from a real-world translation of ~100K lines of C++ to ~90K lines of Rust across ~20 crates. Five key transformation patterns and the architectural decisions behind them.

这是一个真实项目的经验总结:将约 10 万行 C++ 代码迁移到约 9 万行 Rust 代码(分布在约 20 个 crate 中)。我们将探讨五个核心转换模式及其背后的架构决策。

    • We translated a large C++ diagnostic system (~100K lines of C++) into a Rust implementation (~20 Rust crates, ~90K lines)
    • 我们将一个大型 C++ 诊断系统(约 10 万行)转换为了 Rust 实现(分布在约 20 个 crate 中,共约 9 万行)。
    • This section shows the actual patterns used — not toy examples, but real production code
    • 本节展示的是实际使用的模式 —— 不是玩具示例,而是真实的生产代码。
    • The five key transformations:
    • 五个关键的转换:

-| # | C++ Pattern | Rust Pattern | Impact | +| # | C++ Pattern / C++ 模式 | Rust Pattern / Rust 模式 | Impact / 影响 | |—––|––––––––|—————–|———–| -| 1 | Class hierarchy + dynamic_cast | Enum dispatch + match | ~400 → 0 dynamic_casts | +| 1 | Class hierarchy + dynamic_cast / 类继承 | Enum dispatch + match / 枚举分发 | ~400 → 0 dynamic_casts | -| 2 | shared_ptr / enable_shared_from_this tree | Arena + index linkage | No reference cycles | +| 2 | shared_ptr / enable_shared_from_this tree | Arena + index linkage / Arena + 索引关联 | No cycles / 无引用循环 | -| 3 | Framework* raw pointer in every module | DiagContext<'a> with lifetime borrowing | Compile-time validity | +| 3 | Framework* raw pointer / 裸指针 | DiagContext<'a> with lifetime | Compile-time validity / 编译时有效性 | -| 4 | God object | Composable state structs | Testable, modular | +| 4 | God object / 上帝对象 | Composable state structs / 可组合状态结构体 | Testable / 可测试、模块化 | -| 5 | vector<unique_ptr<Base>> everywhere | Trait objects only where needed (~25 uses) | Static dispatch default | +| 5 | vector<unique_ptr<Base>> | Trait objects only if needed / 仅在必要时使用 Trait 对象 | Static dispatch / 默认静态分发 |

-| Metric | C++ (Original) | Rust (Rewrite) | +| Metric / 指标 | C++ (Original / 原版) | Rust (Rewrite / 重写版) | |————|———————|————————| -| dynamic_cast / type downcasts | ~400 | 0 | +| dynamic_cast / type downcasts | ~400 | 0 | -| virtual / override methods | ~900 | ~25 (Box<dyn Trait>) | +| virtual / override methods | ~900 | ~25 (Box<dyn Trait>) | -| Raw new allocations | ~200 | 0 (all owned types) | +| Raw new allocations / 裸 new 分配 | ~200 | 0 (all owned / 全所有权类型) | -| shared_ptr / reference counting | ~10 (topology lib) | 0 (Arc only at FFI boundary) | +| shared_ptr / reference counting | ~10 (topology lib) | 0 (Arc only at FFI / 仅在 FFI 边界) | -| enum class definitions | ~60 | ~190 pub enum | +| enum class definitions | ~60 | ~190 pub enum | -| Pattern matching expressions | N/A | ~750 match | +| Pattern matching / 模式匹配表达式 | N/A | ~750 match | -| God objects (>5K lines) | 2 | 0 | +| God objects / 上帝对象 (>5000 行) | 2 | 0 |


// C++ original: Every GPU event type is a class inheriting from GpuEventBase
+// C++ 原版:每种 GPU 事件类型都是继承自 GpuEventBase 的一个类
class GpuEventBase {
public:
    virtual ~GpuEventBase() = default;
    virtual void Process(DiagFramework* fw) = 0;
    uint16_t m_recordId;
    uint8_t  m_sensorType;
-    // ... common fields
+    // ... common fields / 公共字段
};

class GpuPcieDegradeEvent : public GpuEventBase {
public:
    void Process(DiagFramework* fw) override;
    uint8_t m_linkSpeed;
    uint8_t m_linkWidth;
};

- class GpuPcieFatalEvent : public GpuEventBase { /* ... */ };
- class GpuBootEvent : public GpuEventBase { /* ... */ };
+ class GpuPcieFatalEvent : public GpuEventBase { /* ... */ }; // 致命事件
+ class GpuBootEvent : public GpuEventBase { /* ... */ };      // 启动事件
- // ... 10+ event classes inheriting from GpuEventBase
+ // ... 10+ event classes / 还有 10 多个继承自 GpuEventBase 的事件类

- // Processing requires dynamic_cast:
+ // Processing requires dynamic_cast / 处理时需要 dynamic_cast:
void ProcessEvents(std::vector<std::unique_ptr<GpuEventBase>>& events,
                   DiagFramework* fw) {
    for (auto& event : events) {
        if (auto* degrade = dynamic_cast<GpuPcieDegradeEvent*>(event.get())) {
-            // handle degrade...
+            // handle degrade / 处理降级...
        } else if (auto* fatal = dynamic_cast<GpuPcieFatalEvent*>(event.get())) {
-            // handle fatal...
+            // handle fatal / 处理致命错误...
        }
-        // ... 10 more branches
+        // ... 10 more branches / 还有 10 多个分支
    }
}
#![allow(unused)]
fn main() {
- // Example: types.rs — No inheritance, no vtable, no dynamic_cast
+ // Example: types.rs — No inheritance, no vtable, no dynamic_cast / 示例:无继承、无虚表、无 dynamic_cast
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum GpuEventKind {
    PcieDegrade,
    PcieFatal,
    PcieUncorr,
    Boot,
    BaseboardState,
    EccError,
    OverTemp,
    PowerRail,
    ErotStatus,
    Unknown,
}
}
#![allow(unused)]
fn main() {
- // Example: manager.rs — Separate typed Vecs, no downcasting needed
+ // Example: manager.rs — Separate typed Vecs, no downcasting / 示例:分离的类型化 Vec,理论上不需要向下转型
pub struct GpuEventManager {
    sku: SkuVariant,
-    degrade_events: Vec<GpuPcieDegradeEvent>,   // Concrete type, not Box<dyn>
+    degrade_events: Vec<GpuPcieDegradeEvent>,   // Concrete type / 具体类型,而非 Box<dyn>
    fatal_events: Vec<GpuPcieFatalEvent>,
    uncorr_events: Vec<GpuPcieUncorrEvent>,
    boot_events: Vec<GpuBootEvent>,
    baseboard_events: Vec<GpuBaseboardEvent>,
    ecc_events: Vec<GpuEccEvent>,
-    // ... each event type gets its own Vec
+    // ... each event type / 每个事件类型都有自己的 Vec
}

- // Accessors return typed slices — zero ambiguity
+ // Accessors return typed slices / 访问器返回具体类型的切片 —— 零歧义
impl GpuEventManager {
    pub fn degrade_events(&self) -> &[GpuPcieDegradeEvent] {
        &self.degrade_events
    }
    pub fn fatal_events(&self) -> &[GpuPcieFatalEvent] {
        &self.fatal_events
    }
}
}
    • The Wrong Approach (literal translation): Put all events in one heterogeneous collection, then downcast — this is what C++ does with vector<unique_ptr<Base>>
    • 错误的方法(字面翻译):将所有事件放入一个异类集合中,然后进行向下转型 —— 这正是 C++ 使用 vector<unique_ptr<Base>> 的做法。
    • The Right Approach: Separate typed Vecs eliminate all downcasting. Each consumer asks for exactly the event type it needs
    • 正确的方法:分离的类型化 Vec 消除了所有向下转型。每个消费者只请求它确切需要的事件类型。
    • Performance: Separate Vecs give better cache locality (all degrade events are contiguous in memory)
    • 性能:分离的 Vec 提供了更好的缓存局部性(所有降级事件在内存中都是连续的)。

// C++ topology library: PcieDevice uses enable_shared_from_this 
+// C++ 拓扑库:PcieDevice 使用 enable_shared_from_this
// because parent and child nodes both need to reference each other
+// 因为父节点和子节点需要互相引用
class PcieDevice : public std::enable_shared_from_this<PcieDevice> {
public:
    std::shared_ptr<PcieDevice> m_upstream;
    std::vector<std::shared_ptr<PcieDevice>> m_downstream;
-    // ... device data
+    // ... device data / 设备数据
    
    void AddChild(std::shared_ptr<PcieDevice> child) {
-        child->m_upstream = shared_from_this();  // Parent ↔ child cycle!
+        child->m_upstream = shared_from_this();  // Parent ↔ child cycle / 父子引用循环!
        m_downstream.push_back(child);
    }
};
- // Problem: parent→child and child→parent create reference cycles
+ // Problem / 问题:父→子和子→父引用创建了引用循环
- // Need weak_ptr to break cycles, but easy to forget
+ // 需要用 weak_ptr 来打破循环,但很容易忘记
#![allow(unused)]
fn main() {
- // Example: components.rs — Flat Vec owns all devices
+ // Example: components.rs — Flat Vec owns all devices / 示例:扁平 Vec 拥有所有设备
pub struct PcieDevice {
    pub base: PcieDeviceBase,
    pub kind: PcieDeviceKind,

-    // Tree linkage via indices — no reference counting, no cycles
+    // Tree linkage / 通过索引关联树 —— 无引用计数,无循环
-    pub upstream_idx: Option<usize>,      // Index into the arena Vec
+    pub upstream_idx: Option<usize>,      // Index / Arena Vec 中的索引
-    pub downstream_idxs: Vec<usize>,      // Indices into the arena Vec
+    pub downstream_idxs: Vec<usize>,      // Indices / Arena Vec 中的索引
}

- // The "arena" is simply a Vec<PcieDevice> owned by the tree:
+ // The "arena" is simply a Vec<PcieDevice> owned by the tree / “arena” 本质上是树拥有的一个 Vec<PcieDevice>:
pub struct DeviceTree {
-    devices: Vec<PcieDevice>,  // Flat ownership — one Vec owns everything
+    devices: Vec<PcieDevice>,  // Flat ownership / 扁平化所有权 —— 一个 Vec 拥有所有内容
}

impl DeviceTree {
    pub fn parent(&self, device_idx: usize) -> Option<&PcieDevice> {
        self.devices[device_idx].upstream_idx
            .map(|idx| &self.devices[idx])
    }
    
    pub fn children(&self, device_idx: usize) -> Vec<&PcieDevice> {
        self.devices[device_idx].downstream_idxs
            .iter()
            .map(|&idx| &self.devices[idx])
            .collect()
    }
}
}
    • No shared_ptr, no weak_ptr, no enable_shared_from_this
    • 没有 shared_ptr,没有 weak_ptr,也没有 enable_shared_from_this
    • No reference cycles possible — indices are just usize values
    • 不可能产生引用循环 —— 索引仅仅是 usize 数值。
    • Better cache performance — all devices in contiguous memory
    • 更好的缓存性能 —— 所有设备都存放在连续内存中。
    • Simpler reasoning — one owner (the Vec), many viewers (indices)
    • 更简单的推理 —— 一个所有者(Vec),多个查看者(索引)。
graph LR
-    subgraph "C++ shared_ptr Tree"
+    subgraph "C++ shared_ptr Tree / C++ shared_ptr 树"
-        A1["shared_ptr<Device>"] -->|"shared_ptr"| B1["shared_ptr<Device>"]
+        A1["shared_ptr<Device>"] -->|"shared_ptr"| B1["shared_ptr<Device>"]
-        B1 -->|"shared_ptr (parent)"| A1
+        B1 -->|"shared_ptr (parent)"| A1
-        A1 -->|"shared_ptr"| C1["shared_ptr<Device>"]
+        A1 -->|"shared_ptr"| C1["shared_ptr<Device>"]
-        C1 -->|"shared_ptr (parent)"| A1
+        C1 -->|"shared_ptr (parent)"| A1
         style A1 fill:#ff6b6b,color:#000
         style B1 fill:#ffa07a,color:#000
         style C1 fill:#ffa07a,color:#000
     end
 
-    subgraph "Rust Arena + Index"
+    subgraph "Rust Arena + Index / Rust Arena + 索引"
-        V["Vec<PcieDevice>"]
+        V["Vec<PcieDevice>"]
-        V --> D0["[0] Root<br/>upstream: None<br/>down: [1,2]"]
+        V --> D0["[0] Root / 根<br/>upstream: None<br/>down: [1,2]"]
-        V --> D1["[1] Child<br/>upstream: Some(0)<br/>down: []"]
+        V --> D1["[1] Child / 子<br/>upstream: Some(0)<br/>down: []"]
-        V --> D2["[2] Child<br/>upstream: Some(0)<br/>down: []"]
+        V --> D2["[2] Child / 子<br/>upstream: Some(0)<br/>down: []"]
         style V fill:#51cf66,color:#000
         style D0 fill:#91e5a3,color:#000
         style D1 fill:#91e5a3,color:#000
         style D2 fill:#91e5a3,color:#000
     end

Case Study 3: Framework communication → Lifetime borrowing / 案例研究 3:框架通信 → 生命周期借用

What you’ll learn / 你将学到: How to convert C++ raw-pointer framework communication patterns to Rust’s lifetime-based borrowing system, eliminating dangling pointer risks while maintaining zero-cost abstractions.

如何将 C++ 的裸指针框架通信模式转换为 Rust 基于生命周期的借用系统,在保持零成本抽象的同时消除悬空指针风险。

// C++ original: Every diagnostic module stores a raw pointer to the framework
+// C++ 原版:每个诊断模块都存储一个指向框架的裸指针
class DiagBase {
protected:
-    DiagFramework* m_pFramework;  // Raw pointer — who owns this?
+    DiagFramework* m_pFramework;  // Raw pointer / 裸指针 —— 谁拥有它?
public:
    DiagBase(DiagFramework* fw) : m_pFramework(fw) {}
    
    void LogEvent(uint32_t code, const std::string& msg) {
-        m_pFramework->GetEventLog()->Record(code, msg);  // Hope it's still alive!
+        m_pFramework->GetEventLog()->Record(code, msg);  // Hope alive / 希望它还活着!
    }
};
- // Problem: m_pFramework is a raw pointer with no lifetime guarantee
+ // Problem / 问题:m_pFramework 是一个没有生命周期保证的裸指针
- // If framework is destroyed while modules still reference it → UB
+ // 如果框架被销毁而模块仍在引用它 → 未定义行为(UB)
#![allow(unused)]
fn main() {
- // Example: module.rs — Borrow, don't store
+ // Example: module.rs — Borrow, don't store / 示例:借用,不要存储

- /// Context passed to diagnostic modules during execution.
+ /// Context passed during execution / 执行期间传递给诊断模块的上下文。
- /// The lifetime 'a guarantees the framework outlives the context.
+ /// The lifetime 'a / 生命周期 'a 保证框架的存活时间长于上下文。
pub struct DiagContext<'a> {
    pub der_log: &'a mut EventLogManager,
    pub config: &'a ModuleConfig,
    pub framework_opts: &'a HashMap<String, String>,
}

- /// Modules receive context as a parameter — never store framework pointers
+ /// Modules receive context / 模块通过参数接收上下文 —— 绝不存储框架指针
pub trait DiagModule {
    fn id(&self) -> &str;
    fn execute(&mut self, ctx: &mut DiagContext) -> DiagResult<()>;
    fn pre_execute(&mut self, _ctx: &mut DiagContext) -> DiagResult<()> {
        Ok(())
    }
    fn post_execute(&mut self, _ctx: &mut DiagContext) -> DiagResult<()> {
        Ok(())
    }
}
}
    • C++ modules store a pointer to the framework (danger: what if the framework is destroyed first?)
    • C++ 模块存储一个指向框架的指针(危险:如果框架先被销毁了怎么办?)。
    • Rust modules receive a context as a function parameter — the borrow checker guarantees the framework is alive during the call
    • Rust 模块通过函数参数接收上下文 —— 借用检查器保证框架在调用期间是存活的。
    • No raw pointers, no lifetime ambiguity, no “hope it’s still alive”
    • 没有裸指针,没有生命周期模糊性,也就没有“希望它还活着”这种隐患。

// C++ original: The framework is god object
+// C++ 原版:框架是一个“上帝对象(God Object)”
class DiagFramework {
-    // Health-monitor trap processing
+    // Health-monitor / 健康监测 Trap 处理
    std::vector<AlertTriggerInfo> m_alertTriggers;
    std::vector<WarnTriggerInfo> m_warnTriggers;
    bool m_healthMonHasBootTimeError;
    uint32_t m_healthMonActionCounter;
    
-    // GPU diagnostics
+    // GPU diagnostics / GPU 诊断
    std::map<uint32_t, GpuPcieInfo> m_gpuPcieMap;
    bool m_isRecoveryContext;
    bool m_healthcheckDetectedDevices;
-    // ... 30+ more GPU-related fields
+    // ... 30+ more GPU fields / 还有 30 多个 GPU 相关字段
    
-    // PCIe tree
+    // PCIe tree / PCIe 树
    std::shared_ptr<CPcieTreeLinux> m_pPcieTree;
    
-    // Event logging
+    // Event logging / 事件日志
    CEventLogMgr* m_pEventLogMgr;
    
-    // ... several other methods
+    // ... several other methods / 还有其他若干个方法
    void HandleGpuEvents();
    void HandleNicEvents();
    void RunGpuDiag();
-    // Everything depends on everything
+    // Everything depends on everything / 牵一发而动全身
};
#![allow(unused)]
fn main() {
- // Example: main.rs — State decomposed into focused structs
+ // Example: main.rs — Decomposed state / 示例:分解为聚焦的小型结构体

- #[derive(Default)]
+ #[derive(Default)] // 派生 Default
struct HealthMonitorState {
    alert_triggers: Vec<AlertTriggerInfo>,
    warn_triggers: Vec<WarnTriggerInfo>,
    health_monitor_action_counter: u32,
    health_monitor_has_boot_time_error: bool,
-    // Only health-monitor-related fields
+    // Only health-monitor fields / 仅包含健康监测相关字段
}

#[derive(Default)]
struct GpuDiagState {
    gpu_pcie_map: HashMap<u32, GpuPcieInfo>,
    is_recovery_context: bool,
    healthcheck_detected_devices: bool,
-    // Only GPU-related fields
+    // Only GPU fields / 仅包含 GPU 相关字段
}

- /// The framework composes these states rather than owning everything flat
+ /// Framework composes states / 框架组合这些状态,而不是扁平化地拥有全部字段
struct DiagFramework {
-    ctx: DiagContext,             // Execution context
+    ctx: DiagContext,             // Context / 执行上下文
-    args: Args,                   // CLI arguments
+    args: Args,                   // Args / 命令行参数
-    pcie_tree: Option<DeviceTree>,  // No shared_ptr needed
+    pcie_tree: Option<DeviceTree>,  // Tree / 不需要 shared_ptr
-    event_log_mgr: EventLogManager,   // Owned, not raw pointer
+    event_log_mgr: EventLogManager,   // Log / 拥有所有权的管理器,而非裸指针
    fc_manager: FcManager,        // Fault code management
-    health: HealthMonitorState,   // Health-monitor state — its own struct
+    health: HealthMonitorState,   // Health / 独立的子结构体
-    gpu: GpuDiagState,           // GPU state — its own struct
+    gpu: GpuDiagState,           // GPU / 独立的子结构体
}
}
    • Testability: Each state struct can be unit-tested independently
    • 可测试性:每个状态结构体都可以独立进行单元测试。
    • Readability: self.health.alert_triggers vs m_alertTriggers — clear ownership
    • 可读性self.health.alert_triggersm_alertTriggers 相比,所有权关系更清晰。
    • Fearless refactoring: Changing GpuDiagState can’t accidentally affect health-monitor processing
    • 无畏重构:修改 GpuDiagState 不会意外影响到健康监测的处理流程。
    • No method soup: Functions that only need health-monitor state take &mut HealthMonitorState, not the entire framework
    • 没有“方法大杂烩”:只需要健康监测状态的函数只需接收 &mut HealthMonitorState,而不是整个框架对象。

    • Not everything should be an enum! The diagnostic module plugin system is a genuine use case for trait objects
    • 并非所有东西都该用枚举!诊断模块插件系统是 Trait 对象的真实应用场景。
    • Why? Because diagnostic modules are open for extension — new modules can be added without modifying the framework
    • 为什么?因为诊断模块需要“对扩展开放” —— 可以在不修改框架的前提下添加新模块。
#![allow(unused)]
fn main() {
- // Example: framework.rs — Vec<Box<dyn DiagModule>> is correct here
+ // Example: framework.rs — Box<dyn> is correct / 示例:此处的 Box<dyn DiagModule> 是正确的
pub struct DiagFramework {
-    modules: Vec<Box<dyn DiagModule>>,        // Runtime polymorphism
+    modules: Vec<Box<dyn DiagModule>>,        // Runtime poly / 运行时多态
    pre_diag_modules: Vec<Box<dyn DiagModule>>,
    event_log_mgr: EventLogManager,
-    // ...
+    // // ...
}

impl DiagFramework {
-    /// Register a diagnostic module — any type implementing DiagModule
+    /// Register module / 注册诊断模块 —— 任何实现了 DiagModule 的类型
    pub fn register_module(&mut self, module: Box<dyn DiagModule>) {
        info!("Registering module: {}", module.id());
        self.modules.push(module);
    }
}
}

-| Use Case | Pattern | Why | +| Use Case / 使用场景 | Pattern / 模式 | Why / 理由 | |———––|———–|––––| -| Fixed set of variants known at compile time | enum + match | Exhaustive checking, no vtable | +| Fixed known at compile time / 编译时确定的固定变体集 | enum + match | Exhaustive / 穷尽性检查,无虚表 | -| Hardware event types (Degrade, Fatal, Boot, …) | enum GpuEventKind | All variants known, performance matters | +| HW events / 硬件事件类型 (Degrade, Fatal, …) | enum GpuEventKind | Performance / 所有变体已知,性能至关重要 | -| PCIe device types (GPU, NIC, Switch, …) | enum PcieDeviceKind | Fixed set, each variant has different data | +| PCIe devices / PCIe 设备类型 (GPU, NIC, …) | enum PcieDeviceKind | Fixed set / 固定集合,各变体数据结构不同 | -| Plugin/module system (open for extension) | Box<dyn Trait> | New modules added without modifying framework | +| Plugins/modules / 插件/模块系统 (对扩展开放) | Box<dyn Trait> | Extension / 可在不改动框架的情况下添加新模块 | -| Test mocking | Box<dyn Trait> | Inject test doubles | +| Test mocking / 测试 Mock | Box<dyn Trait> | Test doubles / 注入测试替身 |

  • Given this C++ code:
  • 给定以下 C++ 代码:
class Shape { public: virtual double area() = 0; };
class Circle : public Shape { double r; double area() override { return 3.14*r*r; } };
class Rect : public Shape { double w, h; double area() override { return w*h; } };
std::vector<std::unique_ptr<Shape>> shapes;
  • Question: Should the Rust translation use enum Shape or Vec<Box<dyn Shape>>?
  • 问题:Rust 翻译应该使用 enum Shape 还是 Vec<Box<dyn Shape>>
  • Solution (click to expand)
  • Solution (click to expand) / 解决方案(点击展开)
  • Answer: enum Shape — because the set of shapes is closed (known at compile time). You’d only use Box<dyn Shape> if users could add new shape types at runtime.
  • 答案enum Shape —— 因为形状的集合是封闭的(在编译时已知)。只有当用户可以在运行时添加新的形状类型时,才会使用 Box<dyn Shape>
- // Correct Rust translation:
+ // Correct Rust translation / 正确的 Rust 翻译:
enum Shape {
    Circle { r: f64 },
    Rect { w: f64, h: f64 },
}

impl Shape {
    fn area(&self) -> f64 {
        match self {
            Shape::Circle { r } => std::f64::consts::PI * r * r,
            Shape::Rect { w, h } => w * h,
        }
    }
}

fn main() {
    let shapes: Vec<Shape> = vec![
        Shape::Circle { r: 5.0 },
        Shape::Rect { w: 3.0, h: 4.0 },
    ];
    for shape in &shapes {
        println!("Area: {:.2}", shape.area());
    }
}
- // Output:
+ // Output / 输出:
// Area: 78.54
// Area: 12.00

    1. Default to enum dispatch — In ~100K lines of C++, only ~25 uses of Box<dyn Trait> were genuinely needed (plugin systems, test mocks). The other ~900 virtual methods became enums with match
    1. 默认使用枚举分发 —— 在约 10 万行 C++ 代码中,只有约 25 处真正需要 Box<dyn Trait>(插件系统、测试 Mock)。其余约 900 个虚方法都转换成了带 match 的枚举。
    1. Arena pattern eliminates reference cyclesshared_ptr and enable_shared_from_this are symptoms of unclear ownership. Think about who owns the data first
    1. Arena 模式消除了引用循环 —— shared_ptrenable_shared_from_this 是所有权关系不明确的征兆。请先思考谁才是数据的拥有者
    1. Pass context, don’t store pointers — Lifetime-bounded DiagContext<'a> is safer and clearer than storing Framework* in every module
    1. 传递上下文,而不是存储指针 —— 带生命周期限定的 DiagContext<'a> 比在每个模块中存储一个 Framework* 更安全、更清晰。
    1. Decompose god objects — If a struct has 30+ fields, it’s probably 3-4 structs wearing a trenchcoat
    1. 分解上帝对象 —— 如果一个结构体有 30 多个字段,它大概率是三四个小结构体套在了一件大衣里。
    1. The compiler is your pair programmer — ~400 dynamic_cast calls meant ~400 potential runtime failures. Zero dynamic_cast equivalents in Rust means zero runtime type errors
    1. 编译器是你的结对程序员 —— 约 400 处 dynamic_cast 调用意味着 400 处潜在的运行时失败。而 Rust 中零等效项意味着零运行时类型错误。
    • Lifetime annotations: Getting borrows right takes time when you’re used to raw pointers — but once it compiles, it’s correct
    • 生命周期标注:当你习惯了裸指针时,正确处理借用需要一些时间 —— 但一旦编过,它就是正确的。
    • Fighting the borrow checker: Wanting &mut self in two places at once. Solution: decompose state into separate structs
    • 与借用检查器“搏斗”:想在两处同时持有 &mut self。解决方案:将状态分解为独立的结构体。
    • Resisting literal translation: The temptation to write Vec<Box<dyn Base>> everywhere. Ask: “Is this set of variants closed?” → If yes, use enum
    • 抵制字面翻译:到处都想写 Vec<Box<dyn Base>> 的诱惑。请自问:“这个变体集是封闭的吗?” → 如果是,请用枚举。
    1. Start with a small, self-contained module (not the god object)
    1. 从小型、自包含的模块开始(不要从上帝对象开始)。
    1. Translate data structures first, then behavior
    1. 先翻译数据结构,再翻译行为。
    1. Let the compiler guide you — its error messages are excellent
    1. 让编译器引导你 —— 它的错误提示非常出色。
    1. Reach for enum before dyn Trait
    1. 在考虑 dyn Trait 之前,先考虑 enum

Rust Best Practices Summary / Rust 最佳实践总结

What you’ll learn / 你将学到: Practical guidelines for writing idiomatic Rust — code organization, naming conventions, error handling patterns, and documentation. A quick-reference chapter you’ll return to often.

编写地道(idiomatic)Rust 代码的实用指南 —— 包括代码组织、命名规范、错误处理模式及文档编写。这是一个你会经常查阅的快速参考章节。

    • Prefer small functions: Easy to test and reason about
    • 优先使用小型函数:易于测试和推理。
    • Use descriptive names: calculate_total_price() vs calc()
    • 使用描述性名称:例如 calculate_total_price() 优于 calc()
    • Group related functionality: Use modules and separate files
    • 对相关功能进行分组:使用模块(module)和独立的文件。
    • Write documentation: Use /// for public APIs
    • 编写文档:为公开 API 使用 /// 注释。
    • Avoid unwrap() unless infallible: Only use when you’re 100% certain it won’t panic
    • 除非确定不会出错,否则避免使用 unwrap():仅当你 100% 确定不会发生 panic 时才使用它。
#![allow(unused)]
fn main() {
- // Bad: Can panic
+ // Bad: Can panic / 坏习惯:可能导致 panic
let value = some_option.unwrap();

- // Good: Handle the None case
+ // Good: Handle None / 好习惯:处理 None 的情况
let value = some_option.unwrap_or(default_value);
let value = some_option.unwrap_or_else(|| expensive_computation());
- let value = some_option.unwrap_or_default(); // Uses Default trait
+ let value = some_option.unwrap_or_default(); // 使用 Default trait

- // For Result<T, E>
+ // For Result<T, E> / 针对 Result<T, E>
let value = some_result.unwrap_or(fallback_value);
let value = some_result.unwrap_or_else(|err| {
    eprintln!("Error occurred: {err}");
    default_value
});
}
    • Use expect() with descriptive messages: When unwrap is justified, explain why
    • 使用带有描述性消息的 expect():当使用 unwrap 是合理的时候,请解释理由。
#![allow(unused)]
fn main() {
let config = std::env::var("CONFIG_PATH")
    .expect("CONFIG_PATH environment variable must be set");
}
    • Return Result<T, E> for fallible operations: Let callers decide how to handle errors
    • 对可能失败的操作返回 Result<T, E>:让调用者决定如何处理错误。
    • Use thiserror for custom error types: More ergonomic than manual implementations
    • 为自定义错误类型使用 thiserror:比手动实现更符合人体工程学。
#![allow(unused)]
fn main() {
use thiserror::Error;

#[derive(Error, Debug)]
pub enum MyError {
-    #[error("IO error: {0}")]
+    #[error("IO error: {0}")] // IO 错误
    Io(#[from] std::io::Error),
    
-    #[error("Parse error: {message}")]
+    #[error("Parse error: {message}")] // 解析错误
    Parse { message: String },
    
-    #[error("Value {value} is out of range")]
+    #[error("Value {value} is out of range")] // 数值越界
    OutOfRange { value: i32 },
}
}
    • Chain errors with ? operator: Propagate errors up the call stack
    • 使用 ? 运算符链接错误:将错误沿调用栈向上传播。
    • Prefer thiserror over anyhow: Our team convention is to define explicit error
  • enums with #[derive(thiserror::Error)] so callers can match on specific variants.
  • anyhow::Error is convenient for quick prototyping but erases the error type, making
  • it harder for callers to handle specific failures. Use thiserror for library and
  • production code; reserve anyhow for throwaway scripts or top-level binaries where
  • you only need to print the error.
    • 优先使用 thiserror 而非 anyhow:我们团队的规范是使用 #[derive(thiserror::Error)] 定义明确的错误枚举,以便调用者可以对特定变体进行匹配。anyhow::Error 虽然在快速原型设计时很方便,但它会抹消错误类型,导致调用者难以处理特定的失败情况。在库和生产代码中使用 thiserror;将 anyhow 留给一次性脚本或只需要打印错误信息的顶级二进制程序。
    • When unwrap() is acceptable:
    • 何时 unwrap() 是可以接受的
    • Unit tests: assert_eq!(result.unwrap(), expected)
    • 单元测试assert_eq!(result.unwrap(), expected)
    • Prototyping: Quick and dirty code that you’ll replace
    • 原型设计:那些之后会被替换掉的临时代码
    • Infallible operations: When you can prove it won’t fail
    • 必然成功的操作:当你能证明它绝不会失败时
#![allow(unused)]
fn main() {
let numbers = vec![1, 2, 3];
- let first = numbers.get(0).unwrap(); // Safe: we just created the vec with elements
+ let first = numbers.get(0).unwrap(); // Safe / 安全:我们刚刚创建了带元素的 vec

- // Better: Use expect() with explanation
+ // Better / 更好:使用带有解释的 expect()
let first = numbers.get(0).expect("numbers vec is non-empty by construction");
}
    • Fail fast: Check preconditions early and return errors immediately
    • 及早失效(Fail fast):尽早检查先决条件并立即返回错误。
    • Prefer borrowing over cloning: Use &T instead of cloning when possible
    • 借用优于克隆:尽可能使用 &T 而不是克隆。
    • Use Rc<T> sparingly: Only when you need shared ownership
    • 审慎使用 Rc<T>:仅在确实需要共享所有权时使用。
    • Limit lifetimes: Use scopes {} to control when values are dropped
    • 限制生命周期:使用作用域 {} 来控制值的释放时机。
    • Avoid RefCell<T> in public APIs: Keep interior mutability internal
    • 避免在公开 API 中使用 RefCell<T>:将内部可变性保持在内部。
    • Profile before optimizing: Use cargo bench and profiling tools
    • 先分析(Profile)再优化:使用 cargo bench 和性能分析工具。
    • Prefer iterators over loops: More readable and often faster
    • 迭代器优于循环:更具可读性,通常也更快。
    • Use &str over String: When you don’t need ownership
    • 使用 &str 而非 String:当你不需要所有权时。
    • Consider Box<T> for large stack objects: Move them to heap if needed
    • 对于大型栈对象考虑使用 Box<T>:如果需要,将它们移动到堆上。
  • When creating custom types, consider implementing these fundamental traits to make your types feel native to Rust:
  • 在创建自定义类型时,考虑实现这些基础 trait,让你的类型感觉像是 Rust 原生的:
#![allow(unused)]
fn main() {
use std::fmt;

- #[derive(Debug)]  // Automatic implementation for debugging
+ #[derive(Debug)]  // 为调试自动实现
struct Person {
    name: String,
    age: u32,
}

- // Manual Display implementation for user-facing output
+ // 为面向用户的输出手动实现 Display
impl fmt::Display for Person {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} (age {})", self.name, self.age)
    }
}

- // Usage:
+ // Usage / 用法:
let person = Person { name: "Alice".to_string(), age: 30 };
- println!("{:?}", person);  // Debug: Person { name: "Alice", age: 30 }
+ println!("{:?}", person);  // Debug 输出: Person { name: "Alice", age: 30 }
- println!("{}", person);    // Display: Alice (age 30)
+ println!("{}", person);    // Display 输出: Alice (age 30)
}
#![allow(unused)]
fn main() {
- // Copy: Implicit duplication for small, simple types
+ // Copy: 为小型简单类型提供隐式复制
#[derive(Debug, Clone, Copy)]
struct Point {
    x: i32,
    y: i32,
}

- // Clone: Explicit duplication for complex types
+ // Clone: 为复杂类型提供显式复制
#[derive(Debug, Clone)]
struct Person {
-    name: String,  // String doesn't implement Copy
+    name: String,  // String 没有实现 Copy
    age: u32,
}

let p1 = Point { x: 1, y: 2 };
- let p2 = p1;  // Copy (implicit)
+ let p2 = p1;  // Copy (隐式)

let person1 = Person { name: "Bob".to_string(), age: 25 };
- let person2 = person1.clone();  // Clone (explicit)
+ let person2 = person1.clone();  // Clone (显式)
}
#![allow(unused)]
fn main() {
#[derive(Debug, PartialEq, Eq)]
struct UserId(u64);

#[derive(Debug, PartialEq)]
struct Temperature {
-    celsius: f64,  // f64 doesn't implement Eq (due to NaN)
+    celsius: f64,  // f64 没有实现 Eq (由于 NaN 的存在)
}

let id1 = UserId(123);
let id2 = UserId(123);
- assert_eq!(id1, id2);  // Works because of PartialEq
+ assert_eq!(id1, id2);  // 由于实现了 PartialEq,此处成立

let temp1 = Temperature { celsius: 20.0 };
let temp2 = Temperature { celsius: 20.0 };
- assert_eq!(temp1, temp2);  // Works with PartialEq
+ assert_eq!(temp1, temp2);  // 由于实现了 PartialEq,此处成立
}
#![allow(unused)]
fn main() {
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
struct Priority(u8);

let high = Priority(1);
let low = Priority(10);
- assert!(high < low);  // Lower numbers = higher priority
+ assert!(high < low);  // 数字越小 = 优先级越高

- // Use in collections
+ // 在集合中使用
let mut priorities = vec![Priority(5), Priority(1), Priority(8)];
- priorities.sort();  // Works because Priority implements Ord
+ priorities.sort();  // 由于 Priority 实现了 Ord,此处可以排序
}
#![allow(unused)]
fn main() {
#[derive(Debug, Default)]
struct Config {
-    debug: bool,           // false (default)
+    debug: bool,           // 默认 false
-    max_connections: u32,  // 0 (default)
+    max_connections: u32,  // 默认 0
-    timeout: Option<u64>,  // None (default)
+    timeout: Option<u64>,  // 默认 None
}

- // Custom Default implementation
+ // 手动实现 Default
impl Default for Config {
    fn default() -> Self {
        Config {
            debug: false,
-            max_connections: 100,  // Custom default
+            max_connections: 100,  // 自定义默认值
-            timeout: Some(30),     // Custom default
+            timeout: Some(30),     // 自定义默认值
        }
    }
}

let config = Config::default();
- let config = Config { debug: true, ..Default::default() };  // Partial override
+ let config = Config { debug: true, ..Default::default() };  // 部分覆盖
}
#![allow(unused)]
fn main() {
struct UserId(u64);
struct UserName(String);

- // Implement From, and Into comes for free
+ // 实现 From 之后,Into 也就自动获得了
impl From<u64> for UserId {
    fn from(id: u64) -> Self {
        UserId(id)
    }
}

impl From<String> for UserName {
    fn from(name: String) -> Self {
        UserName(name)
    }
}

impl From<&str> for UserName {
    fn from(name: &str) -> Self {
        UserName(name.to_string())
    }
}

- // Usage:
+ // Usage / 用法:
- let user_id: UserId = 123u64.into();         // Using Into
+ let user_id: UserId = 123u64.into();         // 使用 Into
- let user_id = UserId::from(123u64);          // Using From
+ let user_id = UserId::from(123u64);          // 使用 From
- let username = UserName::from("alice");      // &str -> UserName
+ let username = UserName::from("alice");      // &str 转为 UserName
- let username: UserName = "bob".into();       // Using Into
+ let username: UserName = "bob".into();       // 使用 Into
}
#![allow(unused)]
fn main() {
use std::convert::TryFrom;

struct PositiveNumber(u32);

#[derive(Debug)]
struct NegativeNumberError;

impl TryFrom<i32> for PositiveNumber {
    type Error = NegativeNumberError;
    
    fn try_from(value: i32) -> Result<Self, Self::Error> {
        if value >= 0 {
            Ok(PositiveNumber(value as u32))
        } else {
            Err(NegativeNumberError)
        }
    }
}

- // Usage:
- let positive = PositiveNumber::try_from(42)?;     // Ok(PositiveNumber(42))
- let error = PositiveNumber::try_from(-5);         // Err(NegativeNumberError)
+ // Usage / 用法:
+ let positive = PositiveNumber::try_from(42)?;     // 成功转换
+ let error = PositiveNumber::try_from(-5);         // 转换失败
}
#![allow(unused)]
fn main() {
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
struct User {
    id: u64,
    name: String,
    email: String,
}

- // Automatic JSON serialization/deserialization
+ // 自动 JSON 序列化/反序列化
let user = User {
    id: 1,
    name: "Alice".to_string(),
    email: "alice@example.com".to_string(),
};

let json = serde_json::to_string(&user)?;
let deserialized: User = serde_json::from_str(&json)?;
}
  • For any new type, consider this checklist:
  • 对于任何新类型,请考虑以下检查表:
#![allow(unused)]
fn main() {
#[derive(
-     Debug,          // [OK] Always implement for debugging
+     Debug,          // [推荐] 始终为调试实现
-     Clone,          // [OK] If the type should be duplicatable
+     Clone,          // [依据情况] 如果类型应该能够复制
-     PartialEq,      // [OK] If the type should be comparable
+     PartialEq,      // [依据情况] 如果类型应该能够比较
-     Eq,             // [OK] If comparison is reflexive/transitive
+     Eq,             // [依据情况] 如果比较满足自反性/传递性
-     PartialOrd,     // [OK] If the type has ordering
+     PartialOrd,     // [依据情况] 如果类型具有顺序
-     Ord,            // [OK] If ordering is total
+     Ord,            // [依据情况] 如果顺序是全序(total)
-     Hash,           // [OK] If type will be used as HashMap key
+     Hash,           // [依据情况] 如果类型将用作 HashMap 的键
-     Default,        // [OK] If there's a sensible default value
+     Default,        // [依据情况] 如果存在合理的默认值
)]
struct MyType {
-    // fields...
+    // fields... / 字段...
}

- // Manual implementations to consider:
+ // 值得考虑的手动实现:
- impl Display for MyType { /* user-facing representation */ }
+ impl Display for MyType { /* 面向用户的表示 */ }
- impl From<OtherType> for MyType { /* convenient conversion */ }
+ impl From<OtherType> for MyType { /* 便捷转换 */ }
- impl TryFrom<FallibleType> for MyType { /* fallible conversion */ }
+ impl TryFrom<FallibleType> for MyType { /* 可能失败的转换 */ }
}
    • Don’t implement Copy for types with heap data: String, Vec, HashMap etc.
    • 不要为带有堆数据的类型实现 Copy:如 StringVecHashMap 等。
    • Don’t implement Eq if values can be NaN: Types containing f32/f64
    • 如果值可能为 NaN,不要实现 Eq:如包含 f32/f64 的类型。
    • Don’t implement Default if there’s no sensible default: File handles, network connections
    • 如果没有合理的默认值,不要实现 Default:如文件句柄、网络连接等。
    • Don’t implement Clone if cloning is expensive: Large data structures (consider Rc<T> instead)
    • 如果克隆代价高昂,不要实现 Clone:如大型数据结构(考虑使用 Rc<T> 代替)。

-| Trait | Benefit | When to Use | +| Trait | Benefit / 优势 | When to Use / 何时使用 | |—––|———|———––| -| Debug | println!("{:?}", value) | Always (except rare cases) | +| Debug | {:?} padding / 调试打印 | Always / 始终 (极少数情况除外) | -| Display | println!("{}", value) | User-facing types | +| Display | {} user output / 面向用户的输出 | User-facing types / 用户可见类型 | -| Clone | value.clone() | When explicit duplication makes sense | +| Clone | Explicit duplication / 显式复制 | When sensible / 当显式复制合理时 | -| Copy | Implicit duplication | Small, simple types | +| Copy | Implicit duplication / 隐式复制 | Simple types / 简单类型 | -| PartialEq | == and != operators | Most types | +| PartialEq | == and != / 相等性判别 | Most types / 大多数类型 | -| Eq | Reflexive equality | When equality is mathematically sound | +| Eq | Total equality / 全等性 | When logical / 逻辑上合理时 | -| PartialOrd | <, >, <=, >= | Types with natural ordering | +| PartialOrd | Comparisons / 大小比较 | Natural order / 具有自然顺序时 | -| Ord | sort(), BinaryHeap | When ordering is total | +| Ord | Sorting / 排序支持 | Total order / 全序关系 | -| Hash | HashMap keys | Types used as map keys | +| Hash | HashMap keys / 字典键 | As map keys / 作为 key 时 | -| Default | Default::default() | Types with obvious defaults | +| Default | default() / 默认构造 | Obvious defaults / 有明显默认值时 | -| From/Into | Convenient conversions | Common type conversions | +| From/Into | Conversions / 转换 | Type mapping / 类型转换 | -| TryFrom/TryInto | Fallible conversions | Conversions that can fail | +| TryFrom/TryInto | Fallible / 易错转换 | Can fail / 可能失败的转换 |

Avoiding Excessive clone() / 避免过度使用 clone()

Avoiding excessive clone() / 避免过度使用 clone()

What you’ll learn / 你将学到: Why .clone() is a code smell in Rust, how to restructure ownership to eliminate unnecessary copies, and the specific patterns that signal an ownership design problem.

为什么 .clone() 在 Rust 中被视为一种“代码坏味道”,如何重组所有权以消除不必要的拷贝,以及哪些特定模式预示着所有权设计存在问题。

    • Coming from C++, .clone() feels like a safe default — “just copy it”. But excessive cloning hides ownership problems and hurts performance.
    • 对于 C++ 开发者来说,.clone() 听起来像是一个安全的默认选择 —— “拷贝一份就行”。但过度的克隆会掩盖所有权问题并损害性能。
    • Rule of thumb: If you’re cloning to satisfy the borrow checker, you probably need to restructure ownership instead.
    • 经验法则:如果你克隆是为了满足借用检查器,那么你可能需要重组所有权结构。
#![allow(unused)]
fn main() {
- // BAD: Cloning a String just to pass it to a function that only reads it
+ // BAD: Cloning a String / 坏习惯:为了传参给只读函数而克隆 String
fn log_message(msg: String) {  // Takes ownership unnecessarily
    println!("[LOG] {}", msg);
}
let message = String::from("GPU test passed");
- log_message(message.clone());  // Wasteful: allocates a whole new String
+ log_message(message.clone());  // Wasteful / 浪费:分配了一个全新的 String
- log_message(message);           // Original consumed — clone was pointless
+ log_message(message);           // Original consumed / 原变量被消耗 —— 克隆毫无意义
}
#![allow(unused)]
fn main() {
- // GOOD: Accept a borrow — zero allocation
+ // GOOD: Accept a borrow / 好习惯:接收借用 —— 零分配
fn log_message(msg: &str) {    // Borrows, doesn't own
    println!("[LOG] {}", msg);
}
let message = String::from("GPU test passed");
- log_message(&message);          // No clone, no allocation
+ log_message(&message);          // No clone / 无克隆,无分配
- log_message(&message);          // Can call again — message not consumed
+ log_message(&message);          // Can call again / 可以再次调用 —— 消息未被消耗
}
#![allow(unused)]
fn main() {
- // Example: healthcheck.rs — returns a borrowed view, zero allocation
+ // Example: healthcheck.rs — returns borrowed view / 示例:返回借用视图,零分配
pub fn serial_or_unknown(&self) -> &str {
    self.serial.as_deref().unwrap_or(UNKNOWN_VALUE)
}

pub fn model_or_unknown(&self) -> &str {
    self.model.as_deref().unwrap_or(UNKNOWN_VALUE)
}
}
  • The C++ equivalent would return const std::string& or std::string_view — but in C++ neither is lifetime-checked. In Rust, the borrow checker guarantees the returned &str can’t outlive self.
  • C++ 的对应做法是返回 const std::string&std::string_view —— 但在 C++ 中,这两者都没有经过生命周期检查。而在 Rust 中,借用检查器保证返回的 &str 存活时间不会超过 self
#![allow(unused)]
fn main() {
- // Example: healthcheck.rs — compile-time string tables
+ // Example: healthcheck.rs — string tables / 示例:编译时字符串表
const HBM_SCREEN_RECIPES: &[&str] = &[
    "hbm_ds_ntd", "hbm_ds_ntd_gfx", "hbm_dt_ntd", "hbm_dt_ntd_gfx",
    "hbm_burnin_8h", "hbm_burnin_24h",
];
}
  • In C++ this would typically be std::vector<std::string> (heap-allocated on first use). Rust’s &'static [&'static str] lives in read-only memory — zero runtime cost.
  • 在 C++ 中,这通常是 std::vector<std::string>(首次使用时在堆上分配)。Rust 的 &'static [&'static str] 存储在只读内存中 —— 运行时成本为零。

-| Situation | Why clone is OK | Example | +| Situation / 场景 | Why clone is OK / 理由 | Example / 示例 | |–––––––|––––––––––|———–| -| Arc::clone() for threading | Bumps ref count (~1 ns), doesn’t copy data | let flag = stop_flag.clone(); | -| Arc::clone() for threading | Bumps ref count / 增加引用计数 (~1 ns),不拷贝数据 | let flag = Arc::clone(&flag); | -| Moving data into a spawned thread | Thread needs its own copy | let ctx = ctx.clone(); thread::spawn(move \|\| { ... }) | -| Moving data into thread | Thread needs copy / 线程需要自己的副本 | let ctx = ctx.clone(); thread::spawn(...) | -| Extracting from &self fields | Can’t move out of a borrow | self.name.clone() when returning owned String | -| Extracting from fields / 从字段中提取 | Can’t move out / 无法从借用中移除所有权 | 返回拥有所有权的 String 时 | -| Small Copy types wrapped in Option | .copied() is clearer than .clone() | opt.get(0).copied() for Option<&u32>Option<u32> | -| Small Copy types in Option | .copied() is clearer / 更清晰 | 将 Option<&u32> 转换为 Option<u32> |

#![allow(unused)]
fn main() {
- // Example: workload.rs — Arc::clone is cheap (ref count bump)
+ // Example: workload.rs — Arc::clone is cheap / 示例:Arc::clone 很廉价(仅增加引用计数)
let stop_flag = Arc::new(AtomicBool::new(false));
- let stop_flag_clone = stop_flag.clone();   // ~1 ns, no data copied
+ let stop_flag_clone = stop_flag.clone();   // ~1 ns, no data copied / 不拷贝数据
- let ctx_clone = ctx.clone();               // Clone context for move into thread
+ let ctx_clone = ctx.clone();               // Clone context / 为进入线程克隆上下文

let sensor_handle = thread::spawn(move || {
-    // ...uses stop_flag_clone and ctx_clone
+    // ...uses stop_flag_clone and ctx_clone / 使用副本
});
}
    1. Can I accept &str / &T instead of String / T? → Borrow, don’t clone
    1. 我能否接收 &str / &T 而非 String / T → 借用,不要克隆。
    1. Can I restructure to avoid needing two owners? → Pass by reference or use scopes
    1. 我能否重构以避免需要两个所有者? → 通过引用传递或使用作用域。
    1. Is this Arc::clone()? → That’s fine, it’s O(1)
    1. 这是 Arc::clone() 吗? → 没问题,它是 O(1) 的。
    1. Am I moving data into a thread/closure? → Clone is necessary
    1. 我是否正在将数据移动到线程/闭包中? → 克隆是必要的。
    1. Am I cloning in a hot loop? → Profile and consider borrowing or Cow<T>
    1. 我是否在热点循环中克隆? → 进行性能分析,考虑借用或 Cow<T>

  • Cow (Clone on Write) is an enum that holds either a borrowed reference or
  • Cow(Clone on Write,写时克隆)是一个枚举,它既可以持有借用引用,也可以
  • an owned value. It’s the Rust equivalent of “avoid allocation when possible, but
  • 持有拥有所有权的值。它是 Rust 中“尽可能避免分配,但在需要修改时才分配”的等价方案。
  • allocate if you need to modify.“ C++ has no direct equivalent — the closest is a function
  • that returns const std::string& sometimes and std::string other times.
  • C++ 没有直接的对应物 —— 最接近的是一个有时返回 const std::string&,有时返回 std::string 的函数。
#![allow(unused)]
fn main() {
- // Without Cow — you must choose: always borrow OR always clone
+ // Without Cow — you must choose / 不使用 Cow —— 你必须二选一:始终借用或始终克隆
fn normalize(s: &str) -> String {          // Always allocates!
    if s.contains(' ') {
-        s.replace(' ', "_")               // New String (allocation needed)
+        s.replace(' ', "_")               // New String / 新 String(需要分配)
    } else {
-        s.to_string()                     // Unnecessary allocation!
+        s.to_string()                     // Unnecessary / 不必要的分配!
    }
}

- // With Cow — borrow when unchanged, allocate only when modified
+ // With Cow — borrow when unchanged / 使用 Cow —— 未更改时借用,仅在修改时分配
use std::borrow::Cow;

fn normalize(s: &str) -> Cow<'_, str> {
    if s.contains(' ') {
-        Cow::Owned(s.replace(' ', "_"))    // Allocates (must modify)
+        Cow::Owned(s.replace(' ', "_"))    // Allocates / 分配(必须修改)
    } else {
-        Cow::Borrowed(s)                   // Zero allocation (passthrough)
+        Cow::Borrowed(s)                   // Zero alloc / 零分配(直传)
    }
}
}
use std::borrow::Cow;

- // Cow<'a, str> is essentially:
+ // Cow<'a, str> is essentially / Cow<'a, str> 本质上是:
// enum Cow<'a, str> {
-//     Borrowed(&'a str),     // Zero-cost reference
+//     Borrowed(&'a str),     // Zero-cost / 零成本引用
-//     Owned(String),          // Heap-allocated owned value
+//     Owned(String),          // Managed / 堆分配的拥有所有权的值
// }

fn greet(name: &str) -> Cow<'_, str> {
    if name.is_empty() {
-        Cow::Borrowed("stranger")         // Static string — no allocation
+        Cow::Borrowed("stranger")         // Static / 静态字符串 —— 无分配
    } else if name.starts_with(' ') {
-        Cow::Owned(name.trim().to_string()) // Modified — allocation needed
+        Cow::Owned(name.trim().to_string()) // Modified / 已修改 —— 需要分配
    } else {
-        Cow::Borrowed(name)               // Passthrough — no allocation
+        Cow::Borrowed(name)               // Passthrough / 直传 —— 无分配
    }
}

fn main() {
    let g1 = greet("Alice");     // Cow::Borrowed("Alice")
    let g2 = greet("");          // Cow::Borrowed("stranger")
    let g3 = greet(" Bob ");     // Cow::Owned("Bob")
    
-    // Cow<str> implements Deref<Target = str>, so you can use it as &str:
+    // Cow<str> implements Deref / Cow<str> 实现了 Deref<Target = str>,因此你可以像使用 &str 一样使用它:
-    println!("Hello, {g1}!");    // Works — Cow auto-derefs to &str
+    println!("Hello, {g1}!");    // Works / 正常工作 —— Cow 自动解引用为 &str
    println!("Hello, {g2}!");
    println!("Hello, {g3}!");
}
use std::borrow::Cow;

- /// Normalize a SKU name: trim whitespace, lowercase.
+ /// Normalize SKU / 规范化 SKU 名称:修整空白符、转为小写。
- /// Returns Cow::Borrowed if already normalized (zero allocation).
+ /// Returns Borrowed if already ok / 如果已经是规范化的,则返回 Cow::Borrowed(零分配)。
fn normalize_sku(sku: &str) -> Cow<'_, str> {
    let trimmed = sku.trim();
    if trimmed == sku && sku.chars().all(|c| c.is_lowercase() || !c.is_alphabetic()) {
-        Cow::Borrowed(sku)   // Already normalized — no allocation
+        Cow::Borrowed(sku)   // Already normalized / 已规范化 —— 无分配
    } else {
-        Cow::Owned(trimmed.to_lowercase())  // Needs modification — allocate
+        Cow::Owned(trimmed.to_lowercase())  // Needs / 需要修改 —— 进行分配
    }
}

fn main() {
-    let s1 = normalize_sku("server-x1");   // Borrowed — zero alloc
+    let s1 = normalize_sku("server-x1");   // Borrowed / 借用 —— 零分配
-    let s2 = normalize_sku("  Server-X1 "); // Owned — must allocate
+    let s2 = normalize_sku("  Server-X1 "); // Owned / 拥有所有权 —— 必须分配
    println!("{s1}, {s2}"); // "server-x1, server-x1"
}

-| Situation | Use Cow? | +| Situation / 场景 | Use Cow? / 是否使用? | |–––––––|—————| -| Function returns input unchanged most of the time | ✅ Yes — avoid unnecessary clones | +| Function returns unchanged mostly / 函数大部分时间返回原输入 | ✅ Yes / 是 —— 避免不必要的克隆 | -| Parsing/normalizing strings (trim, lowercase, replace) | ✅ Yes — often input is already valid | +| Parsing (trim, lowercase, replace) / 解析/规范化字符串 | ✅ Yes / 是 —— 通常输入已经是有效的 | -| Always modifying — every code path allocates | ❌ No — just return String | +| Always modifying / 总是修改 —— 每个代码路径都会分配 | ❌ No / 否 —— 直接返回 String | -| Simple pass-through (never modifies) | ❌ No — just return &str | +| Simple pass-through / 简单直传(从不修改) | ❌ No / 否 —— 直接返回 &str | -| Data stored in a struct long-term | ❌ No — use String (owned) | +| Stored in struct long-term / 长期存储在结构体中 | ❌ No / 否 —— 使用 String (拥有所有权) |

  • C++ comparison: Cow<str> is like a function that returns std::variant<std::string_view, std::string>

  • C++ 对比Cow<str> 就像一个返回 std::variant<std::string_view, std::string> 的函数

  • — except with automatic deref and no boilerplate to access the value.

  • —— 不同之处在于它支持自动解引用,访问值时没有繁琐的样板代码。


  • Weak<T> is the Rust equivalent of C++ std::weak_ptr<T>. It holds a non-owning
  • Weak<T> 是 C++ std::weak_ptr<T> 在 Rust 中的等价物。它持有一个对
  • reference to an Rc<T> or Arc<T> value. The value can be deallocated while
  • Rc<T>Arc<T> 值的非拥有性引用。值可以在 Weak 引用仍然存在时
  • Weak references still exist — calling upgrade() returns None if the value is gone.
  • 被释放 —— 如果值已消失,调用 upgrade() 将返回 None
  • Rc<T> and Arc<T> create reference cycles if two values point to each
  • 如果两个值互相指向对方,Rc<T>Arc<T> 会创建引用循环
  • other — neither ever reaches refcount 0, so neither is dropped (memory leak).
  • —— 两者的引用计数永远不会达到 0,因此都不会被释放(导致内存泄漏)。
  • Weak breaks the cycle:
  • Weak 可以打破这种循环:
use std::rc::{Rc, Weak};
use std::cell::RefCell;

#[derive(Debug)]
struct Node {
    value: String,
-    parent: RefCell<Weak<Node>>,      // Weak — doesn't prevent parent from dropping
+    parent: RefCell<Weak<Node>>,      // Weak / 弱引用 —— 不阻止父节点释放
-    children: RefCell<Vec<Rc<Node>>>,  // Strong — parent owns children
+    children: RefCell<Vec<Rc<Node>>>,  // Strong / 强引用 —— 父节点拥有子节点
}

impl Node {
    fn new(value: &str) -> Rc<Node> {
        Rc::new(Node {
            value: value.to_string(),
            parent: RefCell::new(Weak::new()),
            children: RefCell::new(Vec::new()),
        })
    }

    fn add_child(parent: &Rc<Node>, child: &Rc<Node>) {
-        // Child gets a weak reference to parent (no cycle)
+        // Child gets weak ref / 子节点获得父节点的弱引用(无循环)
        *child.parent.borrow_mut() = Rc::downgrade(parent);
-        // Parent gets a strong reference to child
+        // Parent gets strong ref / 父节点获得子节点的强引用
        parent.children.borrow_mut().push(Rc::clone(child));
    }
}

fn main() {
    let root = Node::new("root");
    let child = Node::new("child");
    Node::add_child(&root, &child);

-    // Access parent from child via upgrade()
+    // Access via upgrade() / 通过 upgrade() 访问父节点
    if let Some(parent) = child.parent.borrow().upgrade() {
        println!("Child's parent: {}", parent.value); // "root"
    }
    
    println!("Root strong count: {}", Rc::strong_count(&root));  // 1
    println!("Root weak count: {}", Rc::weak_count(&root));      // 1
}
// C++ — weak_ptr to break shared_ptr cycle
+// C++ — 使用 weak_ptr 打破 shared_ptr 循环
struct Node {
    std::string value;
-    std::weak_ptr<Node> parent;                  // Weak — no ownership
+    std::weak_ptr<Node> parent;                  // Weak / 弱引用 —— 无所有权
-    std::vector<std::shared_ptr<Node>> children;  // Strong — owns children
+    std::vector<std::shared_ptr<Node>> children;  // Strong / 强引用 —— 拥有子节点

    static auto create(const std::string& v) {
        return std::make_shared<Node>(Node{v, {}, {}});
    }
};

auto root = Node::create("root");
auto child = Node::create("child");
- child->parent = root;          // weak_ptr assignment
+ child->parent = root;          // weak_ptr assignment / 弱引用赋值
root->children.push_back(child);

- if (auto p = child->parent.lock()) {   // lock() → shared_ptr or null
+ if (auto p = child->parent.lock()) {   // lock() / lock() → shared_ptr 或 null
    std::cout << "Parent: " << p->value << std::endl;
}

-| C++ | Rust | Notes | +| C++ | Rust | Notes / 说明 | |—–|——|—––| -| shared_ptr<T> | Rc<T> (single-thread) / Arc<T> (multi-thread) | Same semantics | +| shared_ptr<T> | Rc<T> (单线程) / Arc<T> (多线程) | Same semantics / 语义相同 | -| weak_ptr<T> | Weak<T> from Rc::downgrade() / Arc::downgrade() | Same semantics | +| weak_ptr<T> | Weak<T> 来自 downgrade() | Same semantics / 语义相同 | -| weak_ptr::lock()shared_ptr or null | Weak::upgrade()Option<Rc<T>> | None if dropped | +| lock()shared_ptr | upgrade()Option | None / 若已释放则为 None | -| shared_ptr::use_count() | Rc::strong_count() | Same meaning | +| use_count() | strong_count() | Same / 含义相同 |

-| Situation | Pattern | +| Situation / 场景 | Pattern / 模式 | |–––––––|———–| -| Parent ↔ child tree relationships | Parent holds Rc<Child>, child holds Weak<Parent> | -| Parent ↔ child / 父子树关系 | Parent holds Rc, child holds Weak | -| Observer pattern / event listeners | Event source holds Weak<Observer>, observer holds Rc<Source> | -| Observer pattern / 观察者模式 | Source holds Weak, observer holds Rc | -| Cache that doesn’t prevent deallocation | HashMap<Key, Weak<Value>> — entries go stale naturally | -| Cache / 缓存 | HashMap<Key, Weak<Value>> —— 条目会自然失效 | -| Breaking cycles in graph structures | Cross-links use Weak, tree edges use Rc/Arc | -| Graph cycles / 图循环 | Cross-links / 跨链使用 Weak,树边使用 Rc/Arc |

  • Prefer the arena pattern (Case Study 2) over Rc/Weak for tree structures in

  • 首选 Arena 模式(案例研究 2):在新代码的树形结构中,优先使用 Arena 模式而非 Rc/Weak

  • new code. Vec<T> + indices is simpler, faster, and has zero reference-counting

  • overhead. Use Rc/Weak when you need shared ownership with dynamic lifetimes.

  • Vec<T> + 索引更简单、更快,且没有任何引用计数开销。只有当你确实需要具有动态生命周期的共享所有权时,才使用 Rc/Weak


    • Copy ≈ C++ trivially copyable (no custom copy ctor/dtor). Types like int, enum, and simple POD structs — the compiler generates a bitwise memcpy automatically. In Rust, Copy is the same idea: assignment let b = a; does an implicit bitwise copy and both variables remain valid.
    • Copy ≈ C++ trivially copyable(无自定义拷贝构造/析构函数)。intenum 和简单的 POD 结构体 —— 编译器会自动生成按位 memcpy。在 Rust 中,Copy 是同样的概念:赋值语句 let b = a; 会进行隐式按位拷贝,且两个变量都保持有效。
    • Clone ≈ C++ copy constructor / operator= deep-copy. When a C++ class has a custom copy constructor (e.g., to deep-copy a std::vector member), the equivalent in Rust is implementing Clone. You must call .clone() explicitly — Rust never hides an expensive copy behind =.
    • Clone ≈ C++ 拷贝构造函数 / operator= 深拷贝。 当 C++ 类具有自定义拷贝构造函数(例如,为了深拷贝 std::vector 成员)时,Rust 中的等价物是实现 Clone。你必须显式调用 .clone() —— Rust 绝不会在 = 背后隐藏高昂的拷贝开销。
    • Key distinction: In C++, both trivial copies and deep copies happen implicitly via the same = syntax. Rust forces you to choose: Copy types copy silently (cheap), non-Copy types move by default, and you must opt in to an expensive duplicate with .clone().
    • 关键区别:在 C++ 中,平凡拷贝和深拷贝都通过相同的 = 语法隐式发生。Rust 强制你做出选择:Copy 类型静默拷贝(廉价),非 Copy 类型默认进行移动(move),你必须通过 .clone() 显式选择进行昂贵的复制。
    • Similarly, C++ operator== doesn’t distinguish between types where a == a always holds (like integers) and types where it doesn’t (like float with NaN). Rust encodes this in PartialEq vs Eq.
    • 类似地,C++ 的 operator== 不区分那些 a == a 总是成立的类型(如整数)和那些不成立的类型(如带有 NaN 的 float)。Rust 在 PartialEqEq 中体现了这一点。

-| | Copy | Clone | +| | Copy | Clone | |—|———|–––––| -| How it works | Bitwise memcpy (implicit) | Custom logic (explicit .clone()) | -| How it works / 工作原理 | Bitwise / 按位 memcpy (隐式) | Custom / 自定义逻辑 (显式 .clone()) | -| When it happens | On assignment: let b = a; | Only when you call .clone() | -| When it happens / 触发时机 | Assignment / 赋值时 | Explicit call / 显式调用时 | -| After copy/clone | Both a and b are valid | Both a and b are valid | -| After / 之后 | Both valid / 两者均有效 | Both valid / 两者均有效 | -| Without either | let b = a; moves a (a is gone) | let b = a; moves a (a is gone) | -| Without / 若无 | Moves a / 移动 a (a 消失) | Moves a / 移动 a (a 消失) | -| Allowed for | Types with no heap data | Any type | -| Allowed for / 允许用于 | No heap / 无堆数据的类型 | Any type / 任意类型 | -| C++ analogy | Trivially copyable / POD types (no custom copy ctor) | Custom copy constructor (deep copy) | -| C++ 类比 | POD 类型 (无自定义拷贝构造) | 拷贝构造函数 (深拷贝) |

#![allow(unused)]
fn main() {
- // From fan_diag/src/sensor.rs — all unit variants, fits in 1 byte
+ // From fan_diag/src/sensor.rs — fits in 1 byte / 1 字节即可容纳
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum FanStatus {
    #[default]
    Normal,
    Low,
    High,
    Missing,
    Failed,
    Unknown,
}

let status = FanStatus::Normal;
- let copy = status;   // Implicit copy — status is still valid
+ let copy = status;   // Implicit copy / 隐式拷贝 —— status 仍然有效
- println!("{:?} {:?}", status, copy);  // Both work
+ println!("{:?} {:?}", status, copy);  // Both work / 两者均可工作
}
#![allow(unused)]
fn main() {
- // Example: healthcheck.rs — u32 payloads are Copy, so the whole enum is too
+ // Example: healthcheck.rs — u32 is Copy / 示例:u32 是 Copy,所以整个枚举也是
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum HealthcheckStatus {
    Pass,
    ProgramError(u32),
    DmesgError(u32),
    RasError(u32),
    OtherError(u32),
    Unknown,
}
}
#![allow(unused)]
fn main() {
- // Example: components.rs — String prevents Copy
+ // Example: components.rs — String prevents Copy / 示例:String 阻碍了 Copy
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FruData {
    pub technology: DeviceTechnology,
-    pub physical_location: String,      // ← String: heap-allocated, can't Copy
+    pub physical_location: String,      // ← String / 堆分配,无法 Copy
    pub expected: bool,
    pub removable: bool,
}
-// let a = fru_data;   → MOVES (a is gone)
-// let a = fru_data.clone();  → CLONES (fru_data still valid, new heap allocation)
+// let a = fru_data;   → MOVES / 移动 (fru_data 消失)
+// let a = fru_data.clone();  → CLONES / 克隆 (fru_data 仍有效,产生新堆分配)
}
- Does the type contain String, Vec, Box, HashMap,
- Rc, Arc, or any other heap-owning type?
-     YES → Clone only (cannot be Copy)
-     NO  → You CAN derive Copy (and should, if the type is small)
+ 该类型是否包含 String、Vec、Box、HashMap、
+ Rc、Arc 或任何其他拥有堆数据的类型?
+     是 → 只能是 Clone (无法是 Copy)
+     否 → 你可以派生 Copy (如果类型很小,建议派生)

-| | PartialEq | Eq | +| | PartialEq | Eq | |—|–––––––|—––| -| What it gives you | == and != operators | Marker: “equality is reflexive” | -| What it gives you / 带来的功能 | == and != operators / 运算符 | Marker / 标记:“相等性具有自反性” | -| Reflexive? (a == a) | Not guaranteed | Guaranteed | -| Reflexive? / 自反性? | Not guaranteed / 不保证 | Guaranteed / 保证 | -| Why it matters | f32::NAN != f32::NAN | HashMap keys require Eq | -| Why it matters / 重要性 | f32::NAN != f32::NAN | HashMap keys require Eq | -| When to derive | Almost always | When the type has no f32/f64 fields | -| When to derive / 何时派生 | Almost always / 绝大多数情况 | No f32/f64 / 无浮点字段时 | -| C++ analogy | operator== | No direct equivalent (C++ doesn’t check) | -| C++ 类比 | operator== | 无直接对应 (C++ 不检查自反性) |

#![allow(unused)]
fn main() {
- // From hms_trap/src/cpu_handler.rs — Hash requires Eq
+ // From hms_trap/src/cpu_handler.rs — Hash requires Eq / 示例:Hash 需要 Eq
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CpuFaultType {
    InvalidFaultType,
    CpuCperFatalErr,
    CpuLpddr5UceErr,
    CpuC2CUceFatalErr,
-    // ...
+    // // ...
}
-// Used as: HashMap<CpuFaultType, FaultHandler>
-// HashMap keys must be Eq + Hash — PartialEq alone won't compile
+// Usage / 用法:HashMap<CpuFaultType, FaultHandler>
+// HashMap keys must be Eq + Hash / 键必须是 Eq + Hash —— 仅 PartialEq 无法通过编译
}
#![allow(unused)]
fn main() {
- // Example: types.rs — f32 prevents Eq
+ // Example: types.rs — f32 prevents Eq / 示例:f32 阻碍了 Eq
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct TemperatureSensors {
-    pub warning_threshold: Option<f32>,   // ← f32 has NaN ≠ NaN
+    pub warning_threshold: Option<f32>,   // ← f32 has NaN / NaN ≠ NaN
-    pub critical_threshold: Option<f32>,  // ← can't derive Eq
+    pub critical_threshold: Option<f32>,  // ← can't / 无法派生 Eq
    pub sensor_names: Vec<String>,
}
-// Cannot be used as HashMap key. Cannot derive Eq.
-// Because: f32::NAN == f32::NAN is false, violating reflexivity.
+// Cannot be key / 无法用作 HashMap 键。无法派生 Eq。
+// Because / 原因:f32::NAN == f32::NAN 为假,违反了自反性。
}

-| | PartialOrd | Ord | +| | PartialOrd | Ord | |—|—————|––––| -| What it gives you | <, >, <=, >= | .sort(), BTreeMap keys | -| What it gives you / 带来的功能 | <, >, <=, >= / 比较运算符 | .sort(), BTreeMap keys / 排序支持 | -| Total ordering? | No (some pairs may be incomparable) | Yes (every pair is comparable) | -| Total ordering? / 全序关系? | No / 否 (部分值可能无法比较) | Yes / 是 (所有值均可比较) | -| f32/f64? | PartialOrd only (NaN breaks ordering) | Cannot derive Ord | -| f32/f64? | Only PartialOrd / 仅 PartialOrd | Cannot derive / 无法派生 Ord |

#![allow(unused)]
fn main() {
- // From hms_trap/src/fault.rs — variant order defines severity
+ // From hms_trap/src/fault.rs — order defines severity / 示例:变体顺序定义了严重性
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum FaultSeverity {
-    Info,      // lowest  (discriminant 0)
+    Info,      // lowest / 最低 (discriminant 0)
-    Warning,   //         (discriminant 1)
+    Warning,   //         (discriminant 1)
-    Error,     //         (discriminant 2)
+    Error,     //         (discriminant 2)
-    Critical,  // highest (discriminant 3)
+    Critical,  // highest / 最高 (discriminant 3)
}
-// FaultSeverity::Info < FaultSeverity::Critical → true
-// Enables: if severity >= FaultSeverity::Error { escalate(); }
+// Condition / 启用:if severity >= FaultSeverity::Error { escalate(); }
}
#![allow(unused)]
fn main() {
- // Example: orchestration.rs
+ // Example: orchestration.rs / 示例:编排逻辑
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
pub enum GpuDiagLevel {
    #[default]
-    Quick,     // lowest
+    Quick,     // lowest / 最低
    Standard,
    Extended,
-    Full,      // highest
+    Full,      // highest / 最高
}
-// Enables: if requested_level >= GpuDiagLevel::Extended { run_extended_tests(); }
+// Condition / 启用:if requested_level >= GpuDiagLevel::Extended { ... }
}
-                        Your new type
-                             │
-                    Contains String/Vec/Box?
-                       /              \
-                     YES                NO
-                      │                  │
-               Clone only          Clone + Copy
-                      │                  │
-               Contains f32/f64?    Contains f32/f64?
-                 /          \         /          \
-               YES           NO     YES           NO
-                │             │      │             │
-          PartialEq       PartialEq  PartialEq  PartialEq
-          only            + Eq       only       + Eq
-                           │                      │
-                     Need sorting?           Need sorting?
-                       /       \               /       \
-                     YES        NO            YES        NO
-                      │          │              │          │
-                PartialOrd    Done        PartialOrd    Done
-                + Ord                     + Ord
-                      │                        │
-                Need as                  Need as
-                map key?                 map key?
-                   │                        │
-                 + Hash                   + Hash
+                          你的新类型
+                             │
+                    是否包含 String/Vec/Box?
+                       /              \
+                      是               否
+                      │                │
+                  仅 Clone         Clone + Copy
+                      │                │
+                 是否包含 f32/f64?   是否包含 f32/f64?
+                 /          \         /          \
+                是           否       是           否
+                │            │       │            │
+           仅 PartialEq  PartialEq  仅 PartialEq  PartialEq
+                         + Eq                 + Eq
+                          │                    │
+                      需要排序吗?            需要排序吗?
+                       /       \             /       \
+                      是        否           是        否
+                      │         │           │         │
+                PartialOrd     完成    PartialOrd    完成
+                + Ord                  + Ord
+                      │                     │
+                  需要作为                需要作为
+                   Map 键?               Map 键?
+                      │                     │
+                    + Hash                + Hash

-| Type category | Typical derive | Example | +| Type category / 类型类别 | Typical derive / 典型派生 | Example / 示例 | |—————––|––––––––––|————| -| Simple status enum | Copy, Clone, PartialEq, Eq, Default | FanStatus | +| Simple status / 简单状态枚举 | Copy, Clone, PartialEq, Eq, Default | FanStatus | -| Enum used as HashMap key | Copy, Clone, PartialEq, Eq, Hash | CpuFaultType, SelComponent | +| Map key / 用作 HashMap 键 | Copy, Clone, PartialEq, Eq, Hash | CpuFaultType | -| Sortable severity enum | Copy, Clone, PartialEq, Eq, PartialOrd, Ord | FaultSeverity, GpuDiagLevel | -| Severity / 可排序的严重性 | Copy, Clone, PartialEq, Eq, PartialOrd, Ord | FaultSeverity | -| Data struct with Strings | Clone, Debug, Serialize, Deserialize | FruData, OverallSummary | -| Data / 带 String 的结构体 | Clone, Debug, Serialize, Deserialize | FruData | -| Serializable config | Clone, Debug, Default, Serialize, Deserialize | DiagConfig | -| Config / 序列化配置 | Clone, Debug, Default, Serialize, ... | DiagConfig |

Avoiding Unchecked Indexing / 避免未检查索引

Avoiding unchecked indexing / 避免未检查的索引

What you’ll learn / 你将学到: Why vec[i] is dangerous in Rust (panics on out-of-bounds), and safe alternatives like .get(), iterators, and entry() API for HashMap. Replaces C++’s undefined behavior with explicit handling.

为什么 vec[i] 在 Rust 中是危险的(越界时会发生 panic),以及像 .get()、迭代器和 HashMapentry() API 这样的安全替代方案。我们将用显式处理来替代 C++ 中的未定义行为。

    • In C++, vec[i] and map[key] have undefined behavior / auto-insert on missing keys. Rust’s [] panics on out-of-bounds.
    • 在 C++ 中,vec[i]map[key] 在索引越界或键缺失时会产生未定义行为或自动插入。而 Rust 的 [] 在越界时会直接 panic。
    • Rule: Use .get() instead of [] unless you can prove the index is valid.
    • 规则:除非你能证明索引是有效的,否则请使用 .get() 代替 []
// C++ — silent UB or insertion
+// C++ — 静默的未定义行为或插入
std::vector<int> v = {1, 2, 3};
- int x = v[10];        // UB! No bounds check with operator[]
+ int x = v[10];        // UB! No bounds check / 未定义行为!operator[] 不执行边界检查

std::map<std::string, int> m;
- int y = m["missing"]; // Silently inserts key with value 0!
+ int y = m["missing"]; // Auto-insert / 静默插入键并赋值为 0!
#![allow(unused)]
fn main() {
// Rust — safe alternatives
+// Rust — 安全替代方案
let v = vec![1, 2, 3];

- // Bad: panics if index out of bounds
+ // Bad / 坏习惯:如果索引越界会发生 panic
// let x = v[10];

- // Good: returns Option<&i32>
+ // Good / 好习惯:返回 Option<&i32>
- let x = v.get(10);              // None — no panic
+ let x = v.get(10);              // None / 返回 None —— 不会 panic
- let x = v.get(1).copied().unwrap_or(0);  // 2, or 0 if missing
+ let x = v.get(1).copied().unwrap_or(0);  // 结果为 2,如果缺失则为 0
}
#![allow(unused)]
fn main() {
// Example: diagnostics.rs
// Parsing a binary SEL record — buffer might be shorter than expected
+// 解析二进制 SEL 记录 —— 缓冲区可能比预期的短
let sensor_num = bytes.get(7).copied().unwrap_or(0);
let ppin = cpu_ppin.get(i).map(|s| s.as_str()).unwrap_or("");
}
#![allow(unused)]
fn main() {
// Example: profile.rs — double lookup: HashMap → Vec
+// 示例:双重查找:从 HashMap 到 Vec
pub fn get_processor(&self, location: &str) -> Option<&Processor> {
    self.processor_by_location
        .get(location)                              // HashMap → Option<&usize>
        .and_then(|&idx| self.processors.get(idx))   // Vec → Option<&Processor>
}
- // Both lookups return Option — no panics, no UB
+ // Both return Option / 两次查找均返回 Option —— 无 panic,无未定义行为
}
#![allow(unused)]
fn main() {
// Example: framework.rs — every JSON key returns Option
pub let manufacturer = product_fru
-     .get("Manufacturer")            // Option<&Value>
+     .get("Manufacturer")            // Option<&Value> / 获取“Manufacturer”键
-     .and_then(|v| v.as_str())       // Option<&str>
+     .and_then(|v| v.as_str())       // Option<&str> / 尝试转为字符串切片
-     .unwrap_or(UNKNOWN_VALUE)       // &str (safe fallback)
+     .unwrap_or(UNKNOWN_VALUE)       // Safe fallback / 安全的备选值
     .to_string();
}
  • Compare to the C++ pattern: json["SystemInfo"]["ProductFru"]["Manufacturer"] — any missing key throws nlohmann::json::out_of_range.
  • 对比 C++ 模式:json["SystemInfo"]["ProductFru"]["Manufacturer"] —— 任何缺失的键都会抛出 nlohmann::json::out_of_range 异常。
    • After a bounds check: if i < v.len() { v[i] }
    • 在边界检查之后if i < v.len() { v[i] }
    • In tests: Where panicking is the desired behavior
    • 在测试中:此时发生 panic 正是预期的行为。
    • With constants: let first = v[0]; right after assert!(!v.is_empty());
    • 配合常量使用:例如在 assert!(!v.is_empty()); 之后立即使用 let first = v[0];

    • unwrap() panics on None / Err. In production code, prefer the safe alternatives.
    • unwrap() 在遇到 NoneErr 时会发生 panic。在生产代码中,请优先选择安全的替代方案。

-| Method | Behavior on None/Err | Use When | +| Method / 方法 | Behavior on None/Err / 失败时的行为 | Use When / 适用场景 | |———–|————————|———––| -| .unwrap() | Panics | Tests only, or provably infallible | +| .unwrap() | Panics / 发生 Panic | Tests / 仅限测试,或被证明绝不会失败 | -| .expect("msg") | Panics with message | When panic is justified, explain why | +| .expect("msg") | Panic with msg / 带消息 Panic | 理由充分时,并解释原因 | -| .unwrap_or(default) | Returns default | You have a cheap constant fallback | +| .unwrap_or(default) | Returns default / 返回默认值 | 拥有廉价的常量备选值时 | -| .unwrap_or_else(\|\| expr) | Calls closure | Fallback is expensive to compute | +| .unwrap_or_else(...) | Calls closure / 调用闭包 | 备选值的计算开销较大时 | -| .unwrap_or_default() | Returns Default::default() | Type implements Default | +| .unwrap_or_default() | Returns Default / 返回默认值 | 类型实现了 Default 时 |

#![allow(unused)]
fn main() {
// Example: peripherals.rs
// Regex capture groups might not match — provide safe fallbacks
+// 正则捕获组可能不匹配 —— 提供安全的备选方案
let bus_hex = caps.get(1).map(|m| m.as_str()).unwrap_or("00");
let fw_status = caps.get(5).map(|m| m.as_str()).unwrap_or("0x0");
let bus = u8::from_str_radix(bus_hex, 16).unwrap_or(0);
}
#![allow(unused)]
fn main() {
// Example: framework.rs
// Full function wraps logic in an Option-returning closure;
// if anything fails, return a default struct:
+// 将逻辑封装在返回 Option 的闭包中;如果任何一步失败,则返回默认结构体:
(|| -> Option<BaseboardFru> {
    let content = std::fs::read_to_string(path).ok()?;
    let json: serde_json::Value = serde_json::from_str(&content).ok()?;
-    // ... extract fields with .get()? chains
+    // ... extract / 使用 .get()? 链式提取字段
    Some(baseboard_fru)
})()
.unwrap_or_else(|| BaseboardFru {
    manufacturer: String::new(),
    model: String::new(),
    product_part_number: String::new(),
    serial_number: String::new(),
    asset_tag: String::new(),
})
}
#![allow(unused)]
fn main() {
// Example: framework.rs
// If JSON config parsing fails, fall back to Default — no crash
+// 如果 JSON 配置解析失败,则回退到默认值 —— 不会崩溃
Ok(json) => serde_json::from_str(&json).unwrap_or_default(),
}
  • The C++ equivalent would be a try/catch around nlohmann::json::parse() with manual default construction in the catch block.
  • C++ 的对应做法是围绕 nlohmann::json::parse() 使用 try/catch,并在 catch 块中手动构造默认值。

    • These methods on Option and Result let you transform the contained value without unwrapping, replacing nested if/else with linear chains.
    • OptionResult 上的这些方法允许你在不解包的情况下转换其中的值,从而将嵌套的 if/else 替换为线性链式调用。

-| Method | On | Does | C++ Equivalent | +| Method / 方法 | On / 作用于 | Does / 功能 | C++ Equivalent / C++ 等价物 | |———–|—––|———|—————––| -| .map(\|v\| ...) | Option / Result | Transform the Some/Ok value | if (opt) { *opt = transform(*opt); } | +| .map(...) | Option / Result | Transform / 转换内部值 | if (opt) { *opt = ...; } | -| .map_err(\|e\| ...) | Result | Transform the Err value | Adding context to catch block | +| .map_err(...) | Result | Transform Err / 转换错误值 | 为 catch 块添加上下文 | -| .and_then(\|v\| ...) | Option / Result | Chain operations that return Option/Result | Nested if-checks | +| .and_then(...) | Option / Result | Chain fallible ops / 链式调用可能失败的操作 | 嵌套的 if 检查 | -| .find_map(\|v\| ...) | Iterator | find + map in one pass | Loop with if + break | +| .find_map(...) | Iterator | find + map / 一次完成查找与转换 | 带 if + break 的循环 | -| .filter(\|v\| ...) | Option / Iterator | Keep only values matching predicate | if (!predicate) return nullopt; | +| .filter(...) | Option / Iterator | Keep matching / 仅保留符合条件的项 | 谓词判断 | -| .ok()? | Result | Convert Result → Option and propagate None | if (result.has_error()) return nullopt; | +| .ok()? | Result | Result → Option / 错误转为 None | 将错误处理转换为 Option 处理 |

#![allow(unused)]
fn main() {
// Example: framework.rs — finding serial number with fallbacks
+// 示例:通过备选方案查找序列号
let sys_info = json.get("SystemInfo")?;

// Try BaseboardFru.BoardSerialNumber first
+// 首先尝试 BaseboardFru.BoardSerialNumber
if let Some(serial) = sys_info
    .get("BaseboardFru")
    .and_then(|b| b.get("BoardSerialNumber"))
    .and_then(|v| v.as_str())
-    .filter(valid_serial)     // Only accept non-empty, valid serials
+    .filter(valid_serial)     // Only valid / 仅接收非空且有效的序列号
{
    return Some(serial.to_string());
}

// Fallback to BoardFru.SerialNumber
+// 回退到 BoardFru.SerialNumber
sys_info
    .get("BoardFru")
    .and_then(|b| b.get("SerialNumber"))
    .and_then(|v| v.as_str())
    .filter(valid_serial)
-    .map(|s| s.to_string())   // Convert &str → String only if Some
+    .map(|s| s.to_string())   // Only if Some / 仅在为 Some 时将 &str 转为 String
}
  • In C++ this would be a pyramid of if (json.contains("BaseboardFru")) { if (json["BaseboardFru"].contains("BoardSerialNumber")) { ... } }.
  • 在 C++ 中,这会变成一座由 if (json.contains("BaseboardFru")) { if (json["BaseboardFru"].contains("BoardSerialNumber")) { ... } } 构成的“代码金字塔”。
#![allow(unused)]
fn main() {
// Example: context.rs — find SDR record matching sensor + owner
+// 示例:查找与传感器及所有者匹配的 SDR 记录
pub fn find_for_event(&self, sensor_number: u8, owner_id: u8) -> Option<&SdrRecord> {
    self.by_sensor.get(&sensor_number).and_then(|indices| {
        indices.iter().find_map(|&i| {
            let record = &self.records[i];
            if record.sensor_owner_id() == Some(owner_id) {
                Some(record)
            } else {
                None
            }
        })
    })
}
}
  • find_map is find + map fused: it stops at the first match and transforms it. The C++ equivalent is a for loop with an if + break.
  • find_map 是将 findmap 融合在一起:它在第一个匹配项处停止并对其进行转换。C++ 的等效做法是带 if + breakfor 循环。
#![allow(unused)]
fn main() {
// Example: main.rs — add context to errors before propagating
+// 示例:在传播错误之前为其添加上下文
let json_str = serde_json::to_string_pretty(&config)
    .map_err(|e| format!("Failed to serialize config: {}", e))?;
}
  • Transforms a serde_json::Error into a descriptive String error that includes context about what failed.
  • serde_json::Error 转换为包含“哪里失败了”这一上下文的描述性 String 错误。

    • C++ teams typically use nlohmann::json for JSON parsing. Rust uses serde + serde_json — which is more powerful because the JSON schema is encoded in the type system.
    • C++ 团队通常使用 nlohmann::json 进行 JSON 解析。Rust 则使用 serde + serde_json —— 这更强大,因为 JSON 模式(Schema)是编码在类型系统中的。
// C++ with nlohmann::json — runtime field access
+// C++ 使用 nlohmann::json — 运行时字段访问
#include <nlohmann/json.hpp>
using json = nlohmann::json;

struct Fan {
    std::string logical_id;
    std::vector<std::string> sensor_ids;
};

Fan parse_fan(const json& j) {
    Fan f;
-    f.logical_id = j.at("LogicalID").get<std::string>();    // throws if missing
+    f.logical_id = j.at("LogicalID").get<std::string>();    // Throws / 若缺失则抛异常
-    if (j.contains("SDRSensorIdHexes")) {                   // manual default handling
+    if (j.contains("SDRSensorIdHexes")) {                   // Manual / 手动默认值处理
        f.sensor_ids = j["SDRSensorIdHexes"].get<std::vector<std::string>>();
    }
    return f;
}
#![allow(unused)]
fn main() {
// Rust with serde — compile-time schema, automatic field mapping
+// Rust 使用 serde — 编译时模式,自动字段映射
use serde::{Serialize, Deserialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Fan {
    pub logical_id: String,
-    #[serde(rename = "SDRSensorIdHexes", default)]  // JSON key → Rust field
+    #[serde(rename = "SDRSensorIdHexes", default)]  // JSON key -> field / 映射键名并提供默认值
-    pub sensor_ids: Vec<String>,                     // Missing → empty Vec
+    pub sensor_ids: Vec<String>,                     // Missing -> empty / 缺失则为空 Vec
    #[serde(default)]
-    pub sensor_names: Vec<String>,                   // Missing → empty Vec
+    pub sensor_names: Vec<String>,                   // Missing -> empty / 缺失则为空 Vec
}

- // One line replaces the entire parse function:
+ // One line replaces parse / 一行代码即可替代整个解析函数:
let fan: Fan = serde_json::from_str(json_str)?;
}

-| Attribute | Purpose | C++ Equivalent | +| Attribute / 属性 | Purpose / 用途 | C++ Equivalent / C++ 等价物 | |–––––––|————|––––––––––| -| #[serde(default)] | Use Default::default() for missing fields | if (j.contains(key)) { ... } else { default; } | -| #[serde(default)] | 缺失时使用 Default::default() | 包含性检查与手动赋值 | -| #[serde(rename = "Key")] | Map JSON key name to Rust field name | Manual j.at("Key") access | -| #[serde(rename = "Key")] | 重命名 JSON 键名 | 手动的 j.at("Key") 访问 | -| #[serde(flatten)] | Absorb unknown keys into HashMap | for (auto& [k,v] : j.items()) { ... } | -| #[serde(flatten)] | 吸收未知键到 HashMap 中 | 手动遍历并吸收 | -| #[serde(skip)] | Don’t serialize/deserialize this field | Not storing in JSON | -| #[serde(skip)] | 不进行序列化/反序列化 | 跳过存储 | -| #[serde(tag = "type")] | Internally tagged enum (discriminator field) | if (j["type"] == "gpu") { ... } | +| #[serde(tag = "type")] | Enum tag / 内部标签枚举 | 基于字段值的 if/else |

#![allow(unused)]
fn main() {
// Example: diag.rs
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DiagConfig {
    pub sku: SkuConfig,
    #[serde(default)]
-    pub level: DiagLevel,            // Missing → DiagLevel::default()
+    pub level: DiagLevel,            // Missing -> default / 缺失则使用默认级别
    #[serde(default)]
-    pub modules: ModuleConfig,       // Missing → ModuleConfig::default()
+    pub modules: ModuleConfig,       // Missing -> default / 缺失则使用默认配置
    #[serde(default)]
-    pub output_dir: String,          // Missing → ""
+    pub output_dir: String,          // Missing -> empty / 缺失则为空内容
    #[serde(default, flatten)]
-    pub options: HashMap<String, serde_json::Value>,  // Absorbs unknown keys
+    pub options: HashMap<String, serde_json::Value>,  // Absorb / 吸收未知键
}

- // Loading is 3 lines (vs ~20+ in C++ with nlohmann):
+ // Loading / 加载仅需 3 行(对比 C++ nlohmann 约需 20+ 行):
let content = std::fs::read_to_string(path)?;
let config: DiagConfig = serde_json::from_str(&content)?;
Ok(config)
}
#![allow(unused)]
fn main() {
// Example: components.rs
#[derive(Debug, Clone, Serialize, Deserialize)]
- #[serde(tag = "type")]                   // JSON: {"type": "Gpu", "product": ...}
+ #[serde(tag = "type")]                   // JSON 格式示例:{"type": "Gpu", "product": ...}
pub enum PcieDeviceKind {
    Gpu { product: GpuProduct, manufacturer: GpuManufacturer },
    Nic { product: NicProduct, manufacturer: NicManufacturer },
    NvmeDrive { drive_type: StorageDriveType, capacity_gb: u32 },
-    // ... 9 more variants
+    // ... 9 more / 还有 9 种变体
}
- // serde automatically dispatches on the "type" field — no manual if/else chain
+ // Dispatch / serde 会根据 "type" 字段自动分发 —— 无需手动 if/else 链
}
  • The C++ equivalent would be: if (j["type"] == "Gpu") { parse_gpu(j); } else if (j["type"] == "Nic") { parse_nic(j); } ...
  • C++ 的对应做法是:if (j["type"] == "Gpu") { parse_gpu(j); } else if (j["type"] == "Nic") { parse_nic(j); } ...
    • Define a ServerConfig struct that can be deserialized from the following JSON:
    • 定义一个 ServerConfig 结构体,使其能够从以下 JSON 中反序列化:
{
    "hostname": "diag-node-01",
    "port": 8080,
    "debug": true,
    "modules": ["accel_diag", "nic_diag", "cpu_diag"]
}
    • Use #[derive(Deserialize)] and serde_json::from_str() to parse it
    • 使用 #[derive(Deserialize)]serde_json::from_str() 进行解析。
    • Add #[serde(default)] to debug so it defaults to false if missing
    • debug 添加 #[serde(default)],使其在缺失时默认为 false
    • Bonus: Add an enum DiagLevel { Quick, Full, Extended } field with #[serde(default)] that defaults to Quick
    • 加分项:添加一个 enum DiagLevel { Quick, Full, Extended } 字段,并带有 #[serde(default)] 且默认为 Quick
  • Starter code (requires cargo add serde --features derive and cargo add serde_json):
  • 入门代码(需要运行 cargo add serde --features derivecargo add serde_json):
use serde::Deserialize;

- // TODO: Define DiagLevel enum with Default impl
+ // TODO: 定义具有 Default 实现的 DiagLevel 枚举

- // TODO: Define ServerConfig struct with serde attributes
+ // TODO: 定义带有 serde 属性的 ServerConfig 结构体

fn main() {
    let json_input = r#"{
        "hostname": "diag-node-01",
        "port": 8080,
        "debug": true,
        "modules": ["accel_diag", "nic_diag", "cpu_diag"]
    }"#;

-    // TODO: Deserialize and print the config
+    // TODO: 反序列化并打印配置
-    // TODO: Try parsing JSON with "debug" field missing — verify it defaults to false
+    // TODO: 尝试解析缺失 "debug" 字段的 JSON —— 验证其默认是否为 false
}
  • Solution (click to expand)
  • Solution / 解决方案(点击展开)
use serde::Deserialize;

#[derive(Debug, Deserialize, Default)]
enum DiagLevel {
    #[default]
    Quick,
    Full,
    Extended,
}

#[derive(Debug, Deserialize)]
struct ServerConfig {
    hostname: String,
    port: u16,
-    #[serde(default)]       // defaults to false if missing
+    #[serde(default)]       // Missing -> false / 缺失则默认为 false
    debug: bool,
    modules: Vec<String>,
-    #[serde(default)]       // defaults to DiagLevel::Quick if missing
+    #[serde(default)]       // Missing -> Quick / 缺失则默认为 Quick
    level: DiagLevel,
}

fn main() {
    let json_input = r#"{
        "hostname": "diag-node-01",
        "port": 8080,
        "debug": true,
        "modules": ["accel_diag", "nic_diag", "cpu_diag"]
    }"#;

    let config: ServerConfig = serde_json::from_str(json_input)
        .expect("Failed to parse JSON");
    println!("{config:#?}");

-    // Test with missing optional fields
+    // Test / 测试缺失可选字段的情况
    let minimal = r#"{
        "hostname": "node-02",
        "port": 9090,
        "modules": []
    }"#;
    let config2: ServerConfig = serde_json::from_str(minimal)
        .expect("Failed to parse minimal JSON");
-    println!("debug (default): {}", config2.debug);    // false
+    println!("debug (default): {}", config2.debug);    // 结果为 false
-    println!("level (default): {:?}", config2.level);  // Quick
+    println!("level (default): {:?}", config2.level);  // 结果为 Quick
}

Collapsing Assignment Pyramids / 精简层层嵌套的赋值结构

Collapsing assignment pyramids with closures / 精简层层嵌套的赋值结构

What you’ll learn / 你将学到: How Rust’s expression-based syntax and closures flatten deeply-nested C++ if/else validation chains into clean, linear code.

Rust 基于表达式的语法和闭包如何将 C++ 中深层嵌套的 if/else 验证链简化为整洁、线性的代码。

    • C++ often requires multi-block if/else chains to assign variables, especially when validation or fallback logic is involved. Rust’s expression-based syntax and closures collapse these into flat, linear code.
    • 在 C++ 中,为了给变量赋值(特别是涉及验证或备选逻辑时),通常需要编写多个 if/else 代码块。Rust 基于表达式的语法和闭包将这些结构精简为扁平的线性代码。
// C++ — three variables set across a multi-block if/else chain
+// C++ — 在多个 if/else 块中设置三个变量
uint32_t fault_code;
const char* der_marker;
const char* action;
if (is_c44ad) {
    fault_code = 32709; der_marker = "CSI_WARN"; action = "No action";
} else if (error.is_hardware_error()) {
    fault_code = 67956; der_marker = "CSI_ERR"; action = "Replace GPU";
} else {
    fault_code = 32709; der_marker = "CSI_WARN"; action = "No action";
}
#![allow(unused)]
fn main() {
// Rust equivalent:accel_fieldiag.rs
// Single expression assigns all three at once:
+// 单个表达式同时为三个变量赋值:
let (fault_code, der_marker, recommended_action) = if is_c44ad {
    (32709u32, "CSI_WARN", "No action")
} else if error.is_hardware_error() {
    (67956u32, "CSI_ERR", "Replace GPU")
} else {
    (32709u32, "CSI_WARN", "No action")
};
}
// C++ — pyramid of doom for JSON navigation
+// C++ — JSON 导航中的“末日金字塔”
std::string get_part_number(const nlohmann::json& root) {
    if (root.contains("SystemInfo")) {
        auto& sys = root["SystemInfo"];
        if (sys.contains("BaseboardFru")) {
            auto& bb = sys["BaseboardFru"];
            if (bb.contains("ProductPartNumber")) {
                return bb["ProductPartNumber"].get<std::string>();
            }
        }
    }
    return "UNKNOWN";
}
#![allow(unused)]
fn main() {
// Rust equivalent:framework.rs
// Closure + ? operator collapses the pyramid into linear code:
+// 闭包 + ? 运算符将金字塔结构精简为线性代码:
let part_number = (|| -> Option<String> {
    let path = self.args.sysinfo.as_ref()?;
    let content = std::fs::read_to_string(path).ok()?;
    let json: serde_json::Value = serde_json::from_str(&content).ok()?;
    let ppn = json
        .get("SystemInfo")?
        .get("BaseboardFru")?
        .get("ProductPartNumber")?
        .as_str()?;
    Some(ppn.to_string())
})()
.unwrap_or_else(|| "UNKNOWN".to_string());
}
  • The closure creates an Option<String> scope where ? bails early at any step. The .unwrap_or_else() provides the fallback once, at the end.
  • 闭包创建了一个 Option<String> 作用域,其中 ? 可以在任何步骤提前返回。.unwrap_or_else() 则在最后统一提供备选方案。
// C++ — manual loop with intermediate variables
+// C++ — 带有中间变量的手动循环
std::vector<std::tuple<std::vector<std::string>, std::string, std::string>> gpu_info;
for (const auto& [key, info] : gpu_pcie_map) {
    std::vector<std::string> bdfs;
-    // ... parse bdf_path into bdfs
+    // ... parse / 将 bdf_path 解析为 bdfs
    std::string serial = info.serial_number.value_or("UNKNOWN");
    std::string model = info.model_number.value_or(model_name);
    gpu_info.push_back({bdfs, serial, model});
}
#![allow(unused)]
fn main() {
// Rust equivalent:peripherals.rs
// Single chain: values() → map → collect
+// 单个链式调用:values() -> map -> collect
let gpu_info: Vec<(Vec<String>, String, String, String)> = self
    .gpu_pcie_map
    .values()
    .map(|info| {
        let bdfs: Vec<String> = info.bdf_path
            .split(')')
            .filter(|s| !s.is_empty())
            .map(|s| s.trim_start_matches('(').to_string())
            .collect();
        let serial = info.serial_number.clone()
            .unwrap_or_else(|| "UNKNOWN".to_string());
        let model = info.model_number.clone()
            .unwrap_or_else(|| model_name.to_string());
        let gpu_bdf = format!("{}:{}:{}.{}",
            info.bdf.segment, info.bdf.bus, info.bdf.device, info.bdf.function);
        (bdfs, serial, model, gpu_bdf)
    })
    .collect();
}
// C++
+// C++ 示例
std::vector<TestResult*> failures;
for (auto& t : test_results) {
    if (!t.is_pass()) {
        failures.push_back(&t);
    }
}
#![allow(unused)]
fn main() {
// Rust — from accel_diag/src/healthcheck.rs
+// Rust 示例
pub fn failed_tests(&self) -> Vec<&TestResult> {
    self.test_results.iter().filter(|t| !t.is_pass()).collect()
}
}

-| C++ Pattern | Rust Replacement | Key Benefit | +| C++ Pattern / C++ 模式 | Rust Replacement / Rust 替代方案 | Key Benefit / 核心优势 | |––––––––|———————|—————–| -| Multi-block variable assignment | let (a, b) = if ... { } else { }; | All variables bound atomically | +| Multi-block assignment / 多块赋值 | let (a, b) = if ... { } else { }; | Atomic / 变量原子绑定 | -| Nested if (contains) pyramid | IIFE closure with ? operator | Linear, flat, early-exit | +| Nested if pyramid / 嵌套 if 金字塔 | IIFE closure with ? / IIFE 闭包 + ? | Flat, early-exit / 扁平、提前退出 | -| for loop + push_back | .iter().map(\|\|).collect() | No intermediate mut Vec | +| for + push_back | .iter().map().collect() | No intermediate / 无需中间的可变 Vec | -| for + if (cond) continue | .iter().filter(\|\|).collect() | Declarative intent | +| for + if continue | .iter().filter().collect() | Declarative / 声明性意图 | -| for + if + break (find first) | .iter().find_map(\|\|) | Search + transform in one pass | -| for + if + break (查找首个) | .iter().find_map() | One pass / 一次完成搜索与转换 |


  • 🔴 Challenge — integrative exercise combining enums, traits, iterators, error handling, and generics
  • 🔴 挑战 —— 结合了枚举、Trait、迭代器、错误处理和泛型的综合练习
  • This integrative exercise brings together enums, traits, iterators, error handling, and generics. You’ll build a simplified diagnostic event processing pipeline similar to patterns used in production Rust code.
  • 这个综合练习将枚举、Trait、迭代器、错误处理和泛型结合在一起。你将构建一个简化的诊断事件处理流水线,类似于生产环境 Rust 代码中使用的模式。
  • Requirements:
  • 需求:
    1. Define an enum Severity { Info, Warning, Critical } with Display, and a struct DiagEvent containing source: String, severity: Severity, message: String, and fault_code: u32
    1. 定义一个带有 Display 实现的 enum Severity { Info, Warning, Critical },以及一个包含 source: Stringseverity: Severitymessage: Stringfault_code: u32struct DiagEvent
    1. Define a trait EventFilter with a method fn should_include(&self, event: &DiagEvent) -> bool
    1. 定义一个 trait EventFilter,其中包含一个方法 fn should_include(&self, event: &DiagEvent) -> bool
    1. Implement two filters: SeverityFilter (only events >= a given severity) and SourceFilter (only events from a specific source string)
    1. 实现两个过滤器:SeverityFilter(仅包含大于或等于指定严重性的事件)和 SourceFilter(仅包含来自特定源字符串的事件)。
    1. Write a function fn process_events(events: &[DiagEvent], filters: &[&dyn EventFilter]) -> Vec<String> that returns formatted report lines for events that pass all filters
    1. 编写一个函数 fn process_events(events: &[DiagEvent], filters: &[&dyn EventFilter]) -> Vec<String>,返回通过所有过滤器的事件的格式化报告行。
    1. Write a fn parse_event(line: &str) -> Result<DiagEvent, String> that parses lines of the form "source:severity:fault_code:message" (return Err for bad input)
    1. 编写一个 fn parse_event(line: &str) -> Result<DiagEvent, String>,解析格式为 "source:severity:fault_code:message" 的行(输入错误时返回 Err)。
  • Starter code:
  • 入门代码:
use std::fmt;

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
enum Severity {
    Info,
    Warning,
    Critical,
}

impl fmt::Display for Severity {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        todo!()
+        todo!() // TODO: 实现此处
    }
}

#[derive(Debug, Clone)]
struct DiagEvent {
    source: String,
    severity: Severity,
    message: String,
    fault_code: u32,
}

trait EventFilter {
    fn should_include(&self, event: &DiagEvent) -> bool;
}

struct SeverityFilter {
    min_severity: Severity,
}
- // TODO: impl EventFilter for SeverityFilter
+ // TODO: 为 SeverityFilter 实现 EventFilter

struct SourceFilter {
    source: String,
}
- // TODO: impl EventFilter for SourceFilter
+ // TODO: 为 SourceFilter 实现 EventFilter

fn process_events(events: &[DiagEvent], filters: &[&dyn EventFilter]) -> Vec<String> {
-    // TODO: Filter events that pass ALL filters, format as
+    // TODO: 过滤通过所有过滤器的事件,格式化为:
    // "[SEVERITY] source (FC:fault_code): message"
-    todo!()
+    todo!() // TODO: 实现此处
}

fn parse_event(line: &str) -> Result<DiagEvent, String> {
-    // Parse "source:severity:fault_code:message"
-    // Return Err for invalid input
-    todo!()
+    // 解析 "source:severity:fault_code:message"
+    // 错误输入返回 Err
+    todo!() // TODO: 实现此处
}

fn main() {
    let raw_lines = vec![
        "accel_diag:Critical:67956:ECC uncorrectable error detected",
        "nic_diag:Warning:32709:Link speed degraded",
        "accel_diag:Info:10001:Self-test passed",
        "cpu_diag:Critical:55012:Thermal throttling active",
        "accel_diag:Warning:32710:PCIe link width reduced",
    ];

-    // Parse all lines, collect successes and report errors
+    // 解析所有行,收集成功结果并报告错误
    let events: Vec<DiagEvent> = raw_lines.iter()
        .filter_map(|line| match parse_event(line) {
            Ok(e) => Some(e),
            Err(e) => { eprintln!("Parse error: {e}"); None }
        })
        .collect();

-    // Apply filters: only Critical+Warning events from accel_diag
+    // 应用过滤器:仅包含 accel_diag 且严重性在 Warning 及以上的事件
    let sev_filter = SeverityFilter { min_severity: Severity::Warning };
    let src_filter = SourceFilter { source: "accel_diag".to_string() };
    let filters: Vec<&dyn EventFilter> = vec![&sev_filter, &src_filter];

    let report = process_events(&events, &filters);
    for line in &report {
        println!("{line}");
    }
    println!("--- {} event(s) matched ---", report.len());
}
  • Solution (click to expand)
  • Solution / 解决方案(点击展开)
#![allow(unused)]
fn main() {
use std::fmt;
@@ -337,11 +333,11 @@
    for line in &report {
        println!("{line}");
    }
    println!("--- {} event(s) matched ---", report.len());
}
- // Output:
+ // Output / 输出:
// [CRITICAL] accel_diag (FC:67956): ECC uncorrectable error detected
// [WARNING] accel_diag (FC:32710): PCIe link width reduced
// --- 2 event(s) matched ---
}

Logging and Tracing Ecosystem / 日志与追踪生态

Logging and Tracing: syslog/printf → log + tracing / 日志与追踪:syslog/printf → log + tracing

What you’ll learn / 你将学到: Rust’s two-layer logging architecture (facade + backend), the log and tracing crates, structured logging with spans, and how this replaces printf/syslog debugging.

Rust 的两层日志架构(门面 + 后端)、logtracing crate、带有 Span 的结构化日志,以及这些工具如何替代 printf/syslog 调试。

  • C++ diagnostic code typically uses printf, syslog, or custom logging frameworks.
  • C++ 诊断代码通常使用 printfsyslog 或自定义日志框架。
  • Rust has a standardized two-layer logging architecture: a facade crate (log or
  • Rust 具有标准化的两层日志架构:一个门面(Facade) crate(log
  • tracing) and a backend (the actual logger implementation).
  • tracing)和一个后端(Backend)(即实际的日志记录器实现)。
  • The log crate provides macros that mirror syslog severity levels. Libraries use
  • log crate 提供的宏镜像了 syslog 的严重程度级别。库使用
  • log macros; binaries choose a backend:
  • log 宏;二进制程序则通过选择一个后端来实现:
// Cargo.toml
// [dependencies]
// log = "0.4"
// env_logger = "0.11"    # One of many backends
+// env_logger = "0.11"    # 众多后端之一

- use log::{info, warn, error, debug, trace};
+ use log::{info, warn, error, debug, trace}; // 导入不同级别的宏

fn check_sensor(id: u32, temp: f64) {
-    trace!("Reading sensor {id}");           // Finest granularity
+    trace!("Reading sensor {id}");           // Trace / 最细粒度
-    debug!("Sensor {id} raw value: {temp}"); // Development-time detail
+    debug!("Sensor {id} raw value: {temp}"); // Debug / 开发阶段详情

    if temp > 85.0 {
-        warn!("Sensor {id} high temperature: {temp}°C");
+        warn!("Sensor {id} high temperature: {temp}°C"); // Warn / 警告
    }
    if temp > 95.0 {
-        error!("Sensor {id} CRITICAL: {temp}°C — initiating shutdown");
+        error!("Sensor {id} CRITICAL: {temp}°C — initiating shutdown"); // Error / 错误
    }
-    info!("Sensor {id} check complete");     // Normal operation
+    info!("Sensor {id} check complete");     // Info / 正常运行信息
}

fn main() {
-    // Initialize the backend — typically done once in main()
+    // Initialize / 初始化后端 —— 通常在 main() 中执行一次
-    env_logger::init();  // Controlled by RUST_LOG env var
+    env_logger::init();  // Controlled / 由 RUST_LOG 环境变量控制

    check_sensor(0, 72.5);
    check_sensor(1, 91.0);
}
- # Control log level via environment variable
+ # 通过环境变量控制日志级别
- RUST_LOG=debug cargo run          # Show debug and above
+ RUST_LOG=debug cargo run          # 显示 debug 及以上级别
- RUST_LOG=warn cargo run           # Show only warn and error
+ RUST_LOG=warn cargo run           # 仅显示 warn 和 error
- RUST_LOG=my_crate=trace cargo run # Per-module filtering
+ RUST_LOG=my_crate=trace cargo run # 针对特定模块进行过滤
- RUST_LOG=my_crate::gpu=debug,warn cargo run  # Mix levels
+ RUST_LOG=my_crate::gpu=debug,warn cargo run  # 混合级别配置

-| C++ | Rust (log) | Notes | +| C++ | Rust (log) | Notes / 说明 | |—–|———––|—––| -| printf("DEBUG: %s\n", msg) | debug!("{msg}") | Format checked at compile time | +| printf("DEBUG: %s\n", msg) | debug!("{msg}") | Compile-time check / 编译时检查格式 | +| syslog(LOG_ERR, "...") | error!("...") | Backend decides output / 后端决定输出去向 | +| #ifdef DEBUG around log calls | trace! / debug! compiled out at max_level | Zero-cost when disabled / 禁用时零成本 | +| Custom Logger::log(level, msg) | log::info!("...") — all crates use same API | Universal facade / 通用门面,可插拔后端 | +| Per-file log verbosity | RUST_LOG=crate::module=level | Env-based / 基于环境配置,无需重新编译 |

  • tracing extends log with structured fields and spans (timed scopes).
  • tracing 通过结构化字段和 **Span(具有时间属性的作用域)**扩展了 log 的功能。
  • This is especially useful for diagnostics code where you want to track context:
  • 这对于需要跟踪上下文的诊断代码特别有用:
// Cargo.toml
// [dependencies]
// tracing = "0.1"
// tracing-subscriber = { version = "0.3", features = ["env-filter"] }

use tracing::{info, warn, error, instrument, info_span};

- #[instrument(skip(data), fields(gpu_id = gpu_id, data_len = data.len()))]
+ #[instrument(skip(data), fields(gpu_id = gpu_id, data_len = data.len()))] // 自动创建 Span
fn run_gpu_test(gpu_id: u32, data: &[u8]) -> Result<(), String> {
    info!("Starting GPU test");

-    let span = info_span!("ecc_check", gpu_id);
+    let span = info_span!("ecc_check", gpu_id); // 手动创建 Span
-    let _guard = span.enter();  // All logs inside this scope include gpu_id
+    let _guard = span.enter();  // Guard / 此作用域内的所有日志都会包含 gpu_id

    if data.is_empty() {
        error!(gpu_id, "No test data provided");
        return Err("empty data".to_string());
    }

-    // Structured fields — machine-parseable, not just string interpolation
+    // Structured fields / 结构化字段 —— 机器可解析,而不只是字符串插值
    info!(
        gpu_id,
        temp_celsius = 72.5,
        ecc_errors = 0,
        "ECC check passed"
    );

    Ok(())
}

fn main() {
-    // Initialize tracing subscriber
+    // Initialize / 初始化 tracing 订阅者
    tracing_subscriber::fmt()
-        .with_env_filter("debug")  // Or use RUST_LOG env var
+        .with_env_filter("debug")  // Filter / 或使用 RUST_LOG 环境变量
-        .with_target(true)          // Show module path
+        .with_target(true)          // Target / 显示模块路径
-        .with_thread_ids(true)      // Show thread IDs
+        .with_thread_ids(true)      // Thread ID / 显示线程 ID
        .init();

    let _ = run_gpu_test(0, &[1, 2, 3]);
}
  • Output with tracing-subscriber:
  • 使用 tracing-subscriber 的输出示例:
#![allow(unused)]
fn main() {
2026-02-15T10:30:00.123Z DEBUG ThreadId(01) run_gpu_test{gpu_id=0 data_len=3}: my_crate: Starting GPU test
2026-02-15T10:30:00.124Z  INFO ThreadId(01) run_gpu_test{gpu_id=0 data_len=3}:ecc_check{gpu_id=0}: my_crate: ECC check passed gpu_id=0 temp_celsius=72.5 ecc_errors=0
}
  • The #[instrument] attribute automatically creates a span with the function name
  • #[instrument] 属性会自动创建一个包含函数名及其参数的 Span:
  • and its arguments:
#![allow(unused)]
fn main() {
use tracing::instrument;

- #[instrument]
+ #[instrument] // 自动记录参数
fn parse_sel_record(record_id: u16, sensor_type: u8, data: &[u8]) -> Result<(), String> {
-    // Every log inside this function automatically includes:
+    // Included / 此函数内的每条日志都会自动包含:
-    // record_id, sensor_type, and data (if Debug)
+    // record_id, sensor_type 以及 data (如果实现了 Debug)
    tracing::debug!("Parsing SEL record");
    Ok(())
}

- // skip: exclude large/sensitive args from the span
+ // skip: / 跳过:从 Span 中排除大型或敏感参数
- // fields: add computed fields
+ // fields: / 字段:添加计算出的字段
#[instrument(skip(raw_buffer), fields(buf_len = raw_buffer.len()))]
fn decode_ipmi_response(raw_buffer: &[u8]) -> Result<Vec<u8>, String> {
    tracing::trace!("Decoding {} bytes", raw_buffer.len());
    Ok(raw_buffer.to_vec())
}
}

-| Aspect | log | tracing | +| Aspect / 维度 | log | tracing | |––––|—––|———–| -| Complexity | Simple — 5 macros | Richer — spans, fields, instruments | -| Complexity / 复杂度 | Simple / 简单 —— 只有 5 个宏 | Richer / 丰富 —— 包含 Span、字段和 instrument | -| Structured data | String interpolation only | Key-value fields: info!(gpu_id = 0, "msg") | -| Structured / 结构化 | String only / 仅字符串插值 | Key-value / 键值字段:info!(gpu_id = 0, "msg") | -| Timing / spans | No | Yes — #[instrument], span.enter() | -| Timing / Span | ❌ 否 | ✅ 是 —— #[instrument]span.enter() | -| Async support | Basic | First-class — spans propagate across .await | -| Async / 异步支持 | Basic / 基础 | First-class / 一流支持 —— Span 可跨越 .await 传播 | -| Compatibility | Universal facade | Compatible with log (has a log bridge) | -| Compatibility / 兼容性 | Universal / 通用门面 | Compatible / 通过桥接兼容 log | -| When to use | Simple applications, libraries | Diagnostic tools, async code, observability | -| When to use / 场景 | Simple / 简单应用或库 | Tools / 诊断工具、异步代码、可观测性需求 |

  • Recommendation: Use tracing for production diagnostic-style projects (diagnostic tools

  • 建议:对于生产环境中的诊断类项目(具有结构化输出需求的诊断工具),请使用 tracing。对于希望最小化依赖的简单库,请使用 logtracing 包含一个兼容层,因此使用 log 宏的库仍然可以与 tracing 订阅者配合工作。

  • with structured output). Use log for simple libraries where you want minimal

  • dependencies. tracing includes a compatibility layer so libraries using log

  • macros still work with a tracing subscriber.

  • Backend options

-| Backend Crate | Output | Use Case | +| Backend Crate / 后端 Crate | Output / 输出 | Use Case / 使用场景 | |–––––––|––––|–––––| -| env_logger | stderr, colored | Development, simple CLI tools | -| env_logger | stderr, 带颜色 | Development / 开发调试、简单 CLI 工具 | -| tracing-subscriber | stderr, formatted | Production with tracing | -| tracing-subscriber | stderr, 格式化 | Production / 使用 tracing 的生产环境 | -| syslog | System syslog | Linux system services | -| syslog | 系统 syslog | Linux 系统服务 | -| tracing-journald | systemd journal | systemd-managed services | -| tracing-journald | systemd 日志 | systemd 管理的服务 | -| tracing-appender | Rotating log files | Long-running daemons | -| tracing-appender | 轮转日志文件 | Long-running / 长期运行的守护进程 | -| tracing-opentelemetry | OpenTelemetry collector | Distributed tracing | -| tracing-opentelemetry | OpenTelemetry 收集器 | Distributed / 分布式追踪 |

18. C++ to Rust Semantic Deep Dives / 18. C++ 到 Rust 的语义深入对比

C++ → Rust Semantic Deep Dives / C++ → Rust 语义深入对比

What you’ll learn / 你将学到: Detailed mappings for C++ concepts that don’t have obvious Rust equivalents — the four named casts, SFINAE vs trait bounds, CRTP vs associated types, and other common friction points during translation.

针对没有明显 Rust 等效概念的 C++ 特性进行详细映射 —— 包括四种命名转换(Casts)、SFINAE 与 Trait 约束的对比、CRTP 与关联类型的对比,以及翻译过程中的其他常见摩擦点。

  • The sections below map C++ concepts that don’t have an obvious 1:1 Rust
  • 以下各节映射了那些没有明显 1:1 Rust 对应物的 C++ 概念。
  • equivalent. These differences frequently trip up C++ programmers during
  • 这些差异经常会让正在进行翻译工作的 C++ 程序员感到困惑。
  • C++ has four named casts. Rust replaces them with different, more explicit mechanisms:
  • C++ 有四种命名的类型转换。Rust 用不同且更显式的机制替代了它们:
// C++ casting hierarchy
- int i = static_cast<int>(3.14);            // 1. Numeric / up-cast
+ int i = static_cast<int>(3.14);            // 1. Numeric / 数值转换或向上转型
- Derived* d = dynamic_cast<Derived*>(base); // 2. Runtime downcasting
+ Derived* d = dynamic_cast<Derived*>(base); // 2. Runtime / 运行时向下转型
- int* p = const_cast<int*>(cp);              // 3. Cast away const
+ int* p = const_cast<int*>(cp);              // 3. Away const / 去除 const 属性
- auto* raw = reinterpret_cast<char*>(&obj); // 4. Bit-level reinterpretation
+ auto* raw = reinterpret_cast<char*>(&obj); // 4. Bit-level / 位级重解释

-| C++ Cast | Rust Equivalent | Safety | Notes | +| C++ Cast | Rust Equivalent / Rust 等效 | Safety / 安全性 | Notes / 说明 | |–––––|––––––––|––––|—––| -| static_cast (numeric) | as keyword | Safe but can truncate/wrap | let i = 3.14_f64 as i32; — truncates to 3 | -| static_cast (numeric) | as 关键字 | Safe / 但可能截断/回绕 | 3.14 as i32 -> 3 | -| static_cast (numeric, checked) | From/Into | Safe, compile-time verified | let i: i32 = 42_u8.into(); — only widens | -| Checked static_cast | From / Into | Safe / 编译时验证 | 42_u8.into() -> i32 (仅限无损增宽) | -| static_cast (numeric, fallible) | TryFrom/TryInto | Safe, returns Result | let i: u8 = 300_u16.try_into()?; — returns Err | -| Fallible static_cast | TryFrom / TryInto | Safe / 返回 Result | 300_u16.try_into()? -> Err | -| dynamic_cast (downcast) | match on enum / Any::downcast_ref | Safe | Pattern matching for enums; Any for trait objects | -| dynamic_cast | match 枚举 / Any | Safe / 安全 | 枚举用模式匹配;Trait 对象用 Any | -| const_cast | No equivalent | | Rust has no way to cast away &&mut in safe code. Use Cell/RefCell for interior mutability | -| const_cast | 无直接对应 | | 安全代码无法将 & 强制转为 &mut。请使用 Cell/RefCell 实现内部可变性 | -| reinterpret_cast | std::mem::transmute | unsafe | Reinterprets bit pattern. Almost always wrong — prefer from_le_bytes() etc. | -| reinterpret_cast | transmute | unsafe / 不安全 | 重解释位模式。几乎总是错误的 —— 优先使用 from_le_bytes() 等 |

#![allow(unused)]
fn main() {
// Rust equivalents:
+// Rust 等效代码示例:

- // 1. Numeric casts — prefer From/Into over `as`
+ // 1. Numeric casts / 数值转换 —— 优先使用 From/Into 而非 `as`
- let widened: u32 = 42_u8.into();             // Infallible widening — always prefer
+ let widened: u32 = 42_u8.into();             // Infallible / 绝不失败的增宽转换 —— 始终以此为首选
- let truncated = 300_u16 as u8;                // ⚠ Wraps to 44! Silent data loss
+ let truncated = 300_u16 as u8;                // ⚠ Wraps / 回绕至 44!存在静默数据丢失
- let checked: Result<u8, _> = 300_u16.try_into(); // Err — safe fallible conversion
+ let checked: Result<u8, _> = 300_u16.try_into(); // Err / 返回错误 —— 安全的易错转换

- // 2. Downcast: enum (preferred) or Any (when needed for type erasure)
+ // 2. Downcast / 向下转型:枚举(推荐)或 Any(用于类型擦除时)
use std::any::Any;

fn handle_any(val: &dyn Any) {
    if let Some(s) = val.downcast_ref::<String>() {
        println!("Got string: {s}");
    } else if let Some(n) = val.downcast_ref::<i32>() {
        println!("Got int: {n}");
    }
}

- // 3. "const_cast" → interior mutability (no unsafe needed)
+ // 3. "const_cast" -> interior mutability / 内部可变性(无需使用 unsafe)
use std::cell::Cell;
struct Sensor {
-    read_count: Cell<u32>,  // Mutate through &self
+    read_count: Cell<u32>,  // Mutate / 通过 &self 进行修改
}
impl Sensor {
    fn read(&self) -> f64 {
-        self.read_count.set(self.read_count.get() + 1); // &self, not &mut self
+        self.read_count.set(self.read_count.get() + 1); // 是 &self,而非 &mut self
        42.0
    }
}

- // 4. reinterpret_cast → transmute (almost never needed)
+ // 4. reinterpret_cast -> transmute / 几乎不需要使用
- // Prefer safe alternatives:
+ // 优先使用安全替代方案:
- let bytes: [u8; 4] = 0x12345678_u32.to_ne_bytes();  // ✅ Safe
+ let bytes: [u8; 4] = 0x12345678_u32.to_ne_bytes();  // ✅ Safe / 安全
- let val = u32::from_ne_bytes(bytes);                   // ✅ Safe
+ let val = u32::from_ne_bytes(bytes);                   // ✅ Safe / 安全
- // unsafe { std::mem::transmute::<u32, [u8; 4]>(val) } // ❌ Avoid
+ // unsafe { std::mem::transmute::<u32, [u8; 4]>(val) } // ❌ Avoid / 应当避免
}
  • Guideline: In idiomatic Rust, as should be rare (use From/Into

  • 准则:在地道的 Rust 代码中,as 应该很少见(增宽用 From/Into

  • for widening, TryFrom/TryInto for narrowing), transmute should be

  • ,缩窄用 TryFrom/TryInto),transmute 应该是极个别的情况,而且不需要 const_cast 的等效方案,因为内部可变性类型已经让它变得不再必要。

  • exceptional, and const_cast has no equivalent because interior mutability

  • types make it unnecessary.


  • C++ relies heavily on the preprocessor for conditional compilation, constants, and
  • C++ 严重依赖预处理器来进行条件编译、定义常量和生成代码。
  • code generation. Rust replaces all of these with first-class language features.
  • Rust 则用语言的一等公民特性替代了所有这些功能。
// C++
#define MAX_RETRIES 5
#define BUFFER_SIZE (1024 * 64)
- #define SQUARE(x) ((x) * (x))  // Macro — textual substitution, no type safety
+ #define SQUARE(x) ((x) * (x))  // Macro / 宏 —— 文本替换,没有类型安全
#![allow(unused)]
fn main() {
// Rust — type-safe, scoped, no textual substitution
+// Rust — 类型安全、有作用域、无文本替换
const MAX_RETRIES: u32 = 5;
const BUFFER_SIZE: usize = 1024 * 64;
- const fn square(x: u32) -> u32 { x * x }  // Evaluated at compile time
+ const fn square(x: u32) -> u32 { x * x }  // Evaluated / 在编译时求值

- // Can be used in const contexts:
+ // 可用于 const 上下文:
- const AREA: u32 = square(12);  // Computed at compile time
+ const AREA: u32 = square(12);  // Computed / 在编译时计算
static BUFFER: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
}
// C++
#ifdef DEBUG
    log_verbose("Step 1 complete");
#endif

#if defined(LINUX) && !defined(ARM)
    use_x86_path();
#else
    use_generic_path();
#endif
#![allow(unused)]
fn main() {
// Rust — attribute-based conditional compilation
+// Rust — 基于属性的条件编译
#[cfg(debug_assertions)]
fn log_verbose(msg: &str) { eprintln!("[VERBOSE] {msg}"); }

#[cfg(not(debug_assertions))]
- fn log_verbose(_msg: &str) { /* compiled away in release */ }
+ fn log_verbose(_msg: &str) { /* compiled / 在 release 模式下会被删除 */ }

- // Combine conditions:
+ // 组合条件:
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn use_x86_path() { /* ... */ }

#[cfg(not(all(target_os = "linux", target_arch = "x86_64")))]
fn use_generic_path() { /* ... */ }

- // Runtime check (condition is still compile-time, but usable in expressions):
+ // Runtime check / 运行时检查(条件仍然是编译时的,但可用于表达式):
if cfg!(target_os = "windows") {
    println!("Running on Windows");
}
}
- # Cargo.toml — replace #ifdef FEATURE_FOO
+ # Cargo.toml — 用于替代 #ifdef FEATURE_FOO
[features]
default = ["json"]
- json = ["dep:serde_json"]       # Optional dependency
+ json = ["dep:serde_json"]       # Optional / 可选依赖
- verbose-logging = []            # Flag with no extra dependency
+ verbose-logging = []            # Flag / 无额外依赖的标志
- gpu-support = ["dep:cuda-sys"]  # Optional GPU support
+ gpu-support = ["dep:cuda-sys"]  # Optional / 可选的 GPU 支持
#![allow(unused)]
fn main() {
- // Conditional code based on feature flags:
+ // 基于特性标志的条件代码:
#[cfg(feature = "json")]
pub fn parse_config(data: &str) -> Result<Config, Error> {
    serde_json::from_str(data).map_err(Error::from)
}

#[cfg(feature = "verbose-logging")]
macro_rules! verbose {
    ($($arg:tt)*) => { eprintln!("[VERBOSE] {}", format!($($arg)*)); }
}
#[cfg(not(feature = "verbose-logging"))]
macro_rules! verbose {
-    ($($arg:tt)*) => { }; // Compiles to nothing
+    ($($arg:tt)*) => { }; // Compiles / 编译后不产生任何内容
}
}
// C++ — textual substitution, notoriously error-prone
+// C++ — 文本替换,众所周知的容易出错
#define DIAG_CHECK(cond, msg) \
    do { if (!(cond)) { log_error(msg); return false; } } while(0)
#![allow(unused)]
fn main() {
// Rust — hygienic, type-checked, operates on syntax tree
+// Rust — 卫生的(Hygienic)、类型检查、在语法树上操作
macro_rules! diag_check {
    ($cond:expr, $msg:expr) => {
        if !($cond) {
            log_error($msg);
            return Err(DiagError::CheckFailed($msg.to_string()));
        }
    };
}

fn run_test() -> Result<(), DiagError> {
    diag_check!(temperature < 85.0, "GPU too hot");
    diag_check!(voltage > 0.8, "Rail voltage too low");
    Ok(())
}
}

-| C++ Preprocessor | Rust Equivalent | Advantage | +| C++ Preprocessor / C++ 预处理器 | Rust Equivalent / Rust 等效 | Advantage / 优势 | |—————–|––––––––|———–| -| #define PI 3.14 | const PI: f64 = 3.14; | Typed, scoped, visible to debugger | -| #define PI 3.14 | const PI: f64 = 3.14; | 有类型、有作用域、调试器可见 | -| #define MAX(a,b) ((a)>(b)?(a):(b)) | macro_rules! or generic fn max<T: Ord> | No double-evaluation bugs | -| #define MAX(a,b) | macro_rules! 或泛型 max | 无重复求值漏洞 | -| #ifdef DEBUG | #[cfg(debug_assertions)] | Checked by compiler, no typo risk | -| #ifdef DEBUG | #[cfg(debug_assertions)] | 编译器检查,无拼写错误风险 | -| #ifdef FEATURE_X | #[cfg(feature = "x")] | Cargo manages features; dependency-aware | -| #ifdef FEATURE_X | #[cfg(feature = "x")] | Cargo 管理特性,感知依赖 | -| #include "header.h" | mod module; + use module::Item; | No include guards, no circular includes | -| #include "header.h" | mod + use | 无需头文件卫士,无循环导出 | -| #pragma once | Not needed | Each .rs file is a module — included exactly once | -| #pragma once | 不需要 | 每个 .rs 文件就是一个模块 —— 仅包含一次 |


  • In C++, the compilation model revolves around textual inclusion:
  • 在 C++ 中,编译模型围绕着文本包含展开:
// widget.h — every translation unit that uses Widget includes this
+// widget.h — 每个使用 Widget 的编译单元都要包含此文件
#pragma once
#include <string>
#include <vector>

class Widget {
public:
    Widget(std::string name);
    void activate();
private:
    std::string name_;
    std::vector<int> data_;
};
// widget.cpp — separate definition
+// widget.cpp — 分离的定义
#include "widget.h"
Widget::Widget(std::string name) : name_(std::move(name)) {}
void Widget::activate() { /* ... */ }
  • In Rust, there are no header files, no forward declarations, no include guards:
  • 在 Rust 中,没有头文件、没有前向声明、也没有头文件卫士
#![allow(unused)]
fn main() {
// src/widget.rs — declaration AND definition in one file
+// src/widget.rs — 声明和定义都在一个文件中
pub struct Widget {
-    name: String,         // Private by default
+    name: String,         // Default / 默认是私有的
    data: Vec<i32>,
}

impl Widget {
    pub fn new(name: String) -> Self {
        Widget { name, data: Vec::new() }
    }
    pub fn activate(&self) { /* ... */ }
}
}
// src/main.rs — import by module path
+// src/main.rs — 通过模块路径导入
- mod widget;  // Tells compiler to include src/widget.rs
+ mod widget;  // Tells / 告知编译器包含 src/widget.rs
use widget::Widget;

fn main() {
    let w = Widget::new("sensor".to_string());
    w.activate();
}

-| C++ | Rust | Why it’s better | +| C++ | Rust | Why it’s better / 为什么更好 | |—–|——|—————–| -| #include "foo.h" | mod foo; in parent + use foo::Item; | No textual inclusion, no ODR violations | -| #include "foo.h" | mod foo; + use foo; | 无文本包含,无 ODR 违反 | -| #pragma once / include guards | Not needed | Each .rs file is a module — compiled once | -| #pragma once | 不需要 | 每个 .rs 文件即模块,仅编译一次 | -| Forward declarations | Not needed | Compiler sees entire crate; order doesn’t matter | -| Forward declarations | 不需要 | 编译器可见整个 crate;顺序无关紧要 | -| class Foo; (incomplete type) | Not needed | No separate declaration/definition split | -| Incomplete type | 不需要 | 无需声明与定义分离 | -| .h + .cpp for each class | Single .rs file | No declaration/definition mismatch bugs | -| .h + .cpp 模式 | 单个 .rs 文件 | 无声明/定义不一致的漏洞 | -| using namespace std; | use std::collections::HashMap; | Always explicit — no global namespace pollution | -| using namespace | use std::... | 始终显式 —— 无全局命名空间污染 | -| Nested namespace a::b | Nested mod a { mod b { } } or a/b.rs | File system mirrors module tree | -| 嵌套 namespace | 嵌套 mod 或子目录 | 文件系统镜像模块树 |


  • C++ uses friend to grant specific classes or functions access to private members.
  • C++ 使用 friend 授予特定类或函数访问私有成员的权限。
  • Rust has no friend keyword — instead, privacy is module-scoped:
  • Rust 没有 friend 关键字 —— 取而代之的是,私有属性是以模块为作用域的
// C++
class Engine {
-    friend class Car;   // Car can access private members
+    friend class Car;   // Car / Car 类可以访问私有成员
    int rpm_;
    void set_rpm(int r) { rpm_ = r; }
public:
    int rpm() const { return rpm_; }
};
// Rust — items in the same module can access all fields, no `friend` needed
+// Rust — 同一模块中的项可以访问所有字段,不需要 `friend`
mod vehicle {
    pub struct Engine {
-        rpm: u32,  // Private to the module (not to the struct!)
+        rpm: u32,  // Private / 模块内私有(而非结构体内私有!)
    }

    impl Engine {
        pub fn new() -> Self { Engine { rpm: 0 } }
        pub fn rpm(&self) -> u32 { self.rpm }
    }

    pub struct Car {
        engine: Engine,
    }

    impl Car {
        pub fn new() -> Self { Car { engine: Engine::new() } }
        pub fn accelerate(&mut self) {
-            self.engine.rpm = 3000; // ✅ Same module — direct field access
+            self.engine.rpm = 3000; // ✅ Same / 同一模块 —— 直接访问字段
        }
        pub fn rpm(&self) -> u32 {
-            self.engine.rpm  // ✅ Same module — can read private field
+            self.engine.rpm  // ✅ Same / 同一模块 —— 可以读取私有字段
        }
    }
}

fn main() {
    let mut car = vehicle::Car::new();
    car.accelerate();
-    // car.engine.rpm = 9000;  // ❌ Compile error: `engine` is private
+    // car.engine.rpm = 9000;  // ❌ Compile error / 编译错误:`engine` 是私有的
-    println!("RPM: {}", car.rpm()); // ✅ Public method on Car
+    println!("RPM: {}", car.rpm()); // ✅ Public / Car 上的公共方法
}

-| C++ Access | Rust Equivalent | Scope | +| C++ Access / C++ 访问 | Rust Equivalent / Rust 等效 | Scope / 作用域 | |———–|––––––––|—––| -| private | (default, no keyword) | Accessible within the same module only | -| private | (默认,无关键字) | 仅在同一模块内可访问 | -| protected | No direct equivalent | Use pub(super) for parent module access | -| protected | 无直接对应 | 使用 pub(super) 供父模块访问 | -| public | pub | Accessible everywhere | -| public | pub | 随处可访问 | -| friend class Foo | Put Foo in the same module | Module-level privacy replaces friend | -| friend class Foo | 将 Foo 放在同模块 | 模块级私有性替代了 friend | -| — | pub(crate) | Visible within the crate but not to external dependents | -| — | pub(crate) | 对 crate 内可见,但对外部依赖不可见 | -| — | pub(super) | Visible to the parent module only | -| — | pub(super) | 仅对父模块可见 | -| — | pub(in crate::path) | Visible within a specific module subtree | -| — | pub(in 指定路径) | 在特定的模块子树内可见 |

  • Key insight: C++ privacy is per-class. Rust privacy is per-module.

  • 关键见解:C++ 的私有性是基于类的。Rust 的私有性是基于模块的。

  • This means you control access by choosing which types live in the same module —

  • colocated types have full access to each other’s private fields.

  • 这意味着你可以通过选择哪些类型位于同一模块来控制访问权限 —— 处于同一位置的类型可以完全访问对方的私有字段。


  • In C++, volatile tells the compiler not to optimize away reads/writes — typically
  • 在 C++ 中,volatile 告知编译器不要优化掉读/写操作 —— 通常
  • used for memory-mapped hardware registers. Rust has no volatile keyword.
  • 用于内存映射的硬件寄存器。Rust 没有 volatile 关键字。
// C++: volatile for hardware registers
+// C++: 用于硬件寄存器的 volatile
volatile uint32_t* const GPIO_REG = reinterpret_cast<volatile uint32_t*>(0x4002'0000);
- *GPIO_REG = 0x01;              // Write not optimized away
+ *GPIO_REG = 0x01;              // Write / 写入不会被优化掉
- uint32_t val = *GPIO_REG;     // Read not optimized away
+ uint32_t val = *GPIO_REG;     // Read / 读取不会被优化掉
#![allow(unused)]
fn main() {
// Rust: explicit volatile operations — only in unsafe code
+// Rust: 显式的 volatile 操作 —— 仅限 unsafe 代码
use std::ptr;

const GPIO_REG: *mut u32 = 0x4002_0000 as *mut u32;

// SAFETY: GPIO_REG is a valid memory-mapped I/O address.
+// 安全性:GPIO_REG 是一个有效的内存映射 I/O 地址。
unsafe {
-    ptr::write_volatile(GPIO_REG, 0x01);   // Write not optimized away
+    ptr::write_volatile(GPIO_REG, 0x01);   // Write / 写入不会被优化掉
-    let val = ptr::read_volatile(GPIO_REG); // Read not optimized away
+    let val = ptr::read_volatile(GPIO_REG); // Read / 读取不会被优化掉
}
}
  • For concurrent shared state (the other common C++ volatile use), Rust uses atomics:
  • 对于并发共享状态(C++ volatile 的另一种常见用途),Rust 使用原子操作:
// C++: volatile is NOT sufficient for thread safety (common mistake!)
+// C++: volatile 对线程安全是不够的(常见错误!)
- volatile bool stop_flag = false;  // ❌ Data race — UB in C++11+
+ volatile bool stop_flag = false;  // ❌ Data race / 数据竞争 —— C++11+ 中的未定义行为

- // Correct C++:
+ // 正确的 C++:
std::atomic<bool> stop_flag{false};
#![allow(unused)]
fn main() {
// Rust: atomics are the only way to share mutable state across threads
+// Rust: 原子操作是跨线程共享可变状态的唯一方式
use std::sync::atomic::{AtomicBool, Ordering};

static STOP_FLAG: AtomicBool = AtomicBool::new(false);

- // From another thread:
+ // 来自另一个线程:
STOP_FLAG.store(true, Ordering::Release);

- // Check:
+ // 检查:
if STOP_FLAG.load(Ordering::Acquire) {
    println!("Stopping");
}
}

-| C++ Usage | Rust Equivalent | Notes | +| C++ Usage / C++ 用法 | Rust Equivalent / Rust 等效 | Notes / 说明 | |———–|––––––––|—––| -| volatile for hardware registers | ptr::read_volatile / ptr::write_volatile | Requires unsafe — correct for MMIO | -| volatile (hardware) | read_volatile / write_volatile | 需要 unsafe —— 适用于 MMIO | -| volatile for thread signaling | AtomicBool / AtomicU32 etc. | C++ volatile is wrong for this too! | -| volatile (thread) | 原子类型 (AtomicBool 等) | C++ volatile 用于此场景也是错的! | -| std::atomic<T> | std::sync::atomic::AtomicT | Same semantics, same orderings | -| std::atomic<T> | std::sync::atomic::AtomicT | 语义相同,内存次序相同 | -| std::atomic<T>::load(memory_order_acquire) | AtomicT::load(Ordering::Acquire) | 1:1 mapping | -| load(memory_order_acquire) | load(Ordering::Acquire) | 1:1 映射 |


// C++
- const int MAX_RETRIES = 5;                    // Compile-time constant
+ const int MAX_RETRIES = 5;                    // Constant / 编译时常量
- static std::string CONFIG_PATH = "/etc/app";  // Static init — order undefined!
+ static std::string CONFIG_PATH = "/etc/app";  // Static / 静态初始化 —— 顺序未定义!
#![allow(unused)]
fn main() {
// Rust
- const MAX_RETRIES: u32 = 5;                   // Compile-time constant, inlined
+ const MAX_RETRIES: u32 = 5;                   // Constant / 编译时常量,内联
- static CONFIG_PATH: &str = "/etc/app";         // 'static lifetime, fixed address
+ static CONFIG_PATH: &str = "/etc/app";         // 'static / 'static 生命周期,固定地址
}
  • C++ has a well-known problem: global constructors in different translation units
  • C++ 有一个众所周知的问题:不同编译单元中的全局构造函数执行
  • execute in unspecified order. Rust avoids this entirely — static values must
  • 顺序是不确定的。Rust 完全避免了这一点 —— static 值必须是
  • be compile-time constants (no constructors).
  • 编译时常量(无构造函数)。
  • For runtime-initialized globals, use LazyLock (Rust 1.80+) or OnceLock:
  • 对于运行时初始化的全局变量,请使用 LazyLock(Rust 1.80+)或 OnceLock
#![allow(unused)]
fn main() {
use std::sync::LazyLock;

// Equivalent to C++ `static std::regex` — initialized on first access, thread-safe
+// 等效于 C++ `static std::regex` —— 在首次访问时初始化,线程安全
static CONFIG_REGEX: LazyLock<regex::Regex> = LazyLock::new(|| {
    regex::Regex::new(r"^[a-z]+_diag$").expect("invalid regex")
});

fn is_valid_diag(name: &str) -> bool {
-    CONFIG_REGEX.is_match(name)  // First call initializes; subsequent calls are fast
+    CONFIG_REGEX.is_match(name)  // First / 首次调用时初始化;后续调用非常快
}
}
#![allow(unused)]
fn main() {
use std::sync::OnceLock;

// OnceLock: initialized once, can be set from runtime data
+// OnceLock:仅初始化一次,可根据运行时数据进行设置
static DB_CONN: OnceLock<String> = OnceLock::new();

fn init_db(connection_string: &str) {
    DB_CONN.set(connection_string.to_string())
        .expect("DB_CONN already initialized");
}

fn get_db() -> &'static str {
    DB_CONN.get().expect("DB not initialized")
}
}

-| C++ | Rust | Notes | +| C++ | Rust | Notes / 说明 | |—–|——|—––| -| const int X = 5; | const X: i32 = 5; | Both compile-time. Rust requires type annotation | -| const int X = 5; | const X: i32 = 5; | 均为编译时。Rust 必须标注类型 | -| constexpr int X = 5; | const X: i32 = 5; | Rust const is always constexpr | -| constexpr | const | Rust const 始终是 constexpr | -| static int count = 0; (file scope) | static COUNT: AtomicI32 = AtomicI32::new(0); | Mutable statics require unsafe or atomics | -| static (文件作用域) | static COUNT: AtomicI32 | 可变静态变量需要 unsafe 或原子操作 | -| static std::string s = "hi"; | static S: &str = "hi"; or LazyLock<String> | No runtime constructor for simple cases | -| static string | static S: &strLazyLock | 简单场景无需运行时构造函数 | -| static MyObj obj; (complex init) | static OBJ: LazyLock<MyObj> = LazyLock::new(\|\| { ... }); | Thread-safe, lazy, no init order issues | -| static MyObj (复杂初始化) | static OBJ: LazyLock | 线程安全、延迟加载、无初始化顺序问题 | -| thread_local | thread_local! { static X: Cell<u32> = Cell::new(0); } | Same semantics | -| thread_local | thread_local! 宏 | 语义相同 |


  • C++ constexpr marks functions and variables for compile-time evaluation. Rust
  • C++ constexpr 标记函数和变量以进行编译时求值。
  • uses const fn and const for the same purpose:
  • Rust 使用 const fnconst 来达到同样的目的:
// C++
constexpr int factorial(int n) {
    return n <= 1 ? 1 : n * factorial(n - 1);
}
- constexpr int val = factorial(5);  // Computed at compile time → 120
+ constexpr int val = factorial(5);  // Computed / 在编译时计算 → 120
#![allow(unused)]
fn main() {
// Rust
const fn factorial(n: u32) -> u32 {
    if n <= 1 { 1 } else { n * factorial(n - 1) }
}
- const VAL: u32 = factorial(5);  // Computed at compile time → 120
+ const VAL: u32 = factorial(5);  // Computed / 在编译时计算 → 120

- // Also works in array sizes and match patterns:
+ // 也适用于数组大小和匹配模式:
const LOOKUP: [u32; 5] = [factorial(1), factorial(2), factorial(3),
                           factorial(4), factorial(5)];
}

-| C++ | Rust | Notes | +| C++ | Rust | Notes / 说明 | |—–|——|—––| -| constexpr int f() | const fn f() -> i32 | Same intent — compile-time evaluable | -| constexpr f() | const fn f() | 意图相同 —— 编译时可求值 | -| constexpr variable | const variable | Rust const is always compile-time | -| constexpr 变量 | const 变量 | Rust const 始终是编译时的 | -| consteval (C++20) | No equivalent | const fn can also run at runtime | -| consteval (C++20) | 无直接对应 | const fn 也可以在运行时运行 | -| if constexpr (C++17) | No equivalent (use cfg! or generics) | Trait specialization fills some use cases | -| if constexpr (C++17) | 无直接对应 (用 cfg! 或泛型) | Trait 特化可覆盖部分用例 | -| constinit (C++20) | static with const initializer | Rust static must be const-initialized by default | -| constinit (C++20) | 带 const 初始值的 static | Rust static 默认必须用 const 初始化 |

  • Current limitations of const fn (stabilized as of Rust 1.82):

  • const fn 的当前限制(截至 Rust 1.82 已稳定):

    • No trait methods (can’t call .len() on a Vec in const context)
    • 无 Trait 方法(不能在 const 上下文中对 Vec 调用 .len()
    • No heap allocation (Box::new, Vec::new not const)
    • 无堆分配(Box::newVec::new 均不是 const)
    • No floating-point arithmeticstabilized in Rust 1.82
    • 无浮点运算 —— Rust 1.82 已稳定
    • Can’t use for loops (use recursion or while with manual index)
    • 不能使用 for 循环(使用递归或带有手动索引的 while

  • In C++, SFINAE (Substitution Failure Is Not An Error) is the mechanism behind
  • 在 C++ 中,SFINAE(替换失败并非错误)是
  • conditional generic programming. It is powerful but notoriously unreadable. Rust
  • 条件泛型编程背后的机制。它很强大,但众所周知的难以阅读。
  • replaces it entirely with trait bounds:
  • Rust 完全用 Trait 约束 替代了它:
// C++: SFINAE-based conditional function (pre-C++20)
+// C++: 基于 SFINAE 的条件函数 (C++20 之前)
template<typename T,
         std::enable_if_t<std::is_integral_v<T>, int> = 0>
T double_it(T val) { return val * 2; }

template<typename T,
         std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
T double_it(T val) { return val * 2.0; }

- // C++20 concepts — cleaner but still verbose:
+ // C++20 concepts — 更加清晰但依然繁琐:
template<std::integral T>
T double_it(T val) { return val * 2; }
#![allow(unused)]
fn main() {
// Rust: trait bounds — readable, composable, excellent error messages
+// Rust: Trait 约束 —— 易读、可组合、错误消息极佳
use std::ops::Mul;

fn double_it<T: Mul<Output = T> + From<u8>>(val: T) -> T {
    val * T::from(2)
}

- // Or with where clause for complex bounds:
+ // 或者针对复杂约束使用 where 子句:
fn process<T>(val: T) -> String
where
    T: std::fmt::Display + Clone + Send,
{
    format!("Processing: {}", val)
}

- // Conditional behavior via separate impls (replaces SFINAE overloads):
+ // 通过分离的 impl 实现条件行为(替换 SFINAE 重载):
trait Describable {
    fn describe(&self) -> String;
}

impl Describable for u32 {
    fn describe(&self) -> String { format!("integer: {self}") }
}

impl Describable for f64 {
    fn describe(&self) -> String { format!("float: {self:.2}") }
}
}

-| C++ Template Metaprogramming | Rust Equivalent | Readability | +| C++ Template Metaprogramming / C++ 模板元编程 | Rust Equivalent / Rust 等效 | Readability / 易读性 | |—————————–|––––––––|———––| -| std::enable_if_t<cond> | where T: Trait | 🟢 Clear English | -| enable_if_t | where T: Trait | 🟢 清晰的类自然语言 | -| std::is_integral_v<T> | Bound on a numeric trait or specific types | 🟢 No _v / _t suffixes | -| is_integral_v | 针对数值 Trait 或特定类型的约束 | 🟢 无 _v / _t 后缀 | -| SFINAE overload sets | Separate impl Trait for ConcreteType blocks | 🟢 Each impl stands alone | -| SFINAE 重载集合 | 独立的 impl Trait for 类型 块 | 🟢 每个实现各司其职 | -| if constexpr (std::is_same_v<T, int>) | Specialization via trait impls | 🟢 Compile-time dispatched | -| if constexpr 类型判断 | 通过 Trait 实现进行特化 | 🟢 编译时分发 | -| C++20 concept | trait | 🟢 Nearly identical intent | -| C++20 concept | trait | 🟢 意图几乎一致 | -| requires clause | where clause | 🟢 Same position, similar syntax | -| requires 子句 | where 子句 | 🟢 相同位置,语法相似 | -| Compilation fails deep inside template | Compilation fails at the call site with trait mismatch | 🟢 No 200-line error cascades | -| 模板深处编译失败 | 在调用处因 Trait 不匹配而失败 | 🟢 无 200 行错误连锁 |

  • Key insight: C++ concepts (C++20) are the closest thing to Rust traits.

  • 关键见解:C++ 概念(C++20)是与 Rust Trait 最接近的东西。

  • If you’re familiar with C++20 concepts, think of Rust traits as concepts

  • that have been a first-class language feature since 1.0, with a coherent

  • implementation model (trait impls) instead of duck typing.

  • 如果你熟悉 C++20 概念,可以将 Rust Trait 视作为自 1.0 以来就是一等公民特性的概念,它具有连贯的实现模型(Trait 实现)而非鸭子类型。


  • C++ std::function<R(Args...)> is a type-erased callable. Rust has three options,
  • C++ std::function<R(Args...)> 是一个类型擦除的可调用对象。Rust 有三种选择,
  • each with different trade-offs:
  • 每种都有不同的权衡:
// C++: one-size-fits-all (heap-allocated, type-erased)
+// C++: 一站式方案(堆分配、类型擦除)
#include <functional>
std::function<int(int)> make_adder(int n) {
    return [n](int x) { return x + n; };
}
#![allow(unused)]
fn main() {
// Rust Option 1: fn pointer — simple, no captures, no allocation
+// Rust 选项 1:fn 指针 —— 简单、无捕获、无分配
fn add_one(x: i32) -> i32 { x + 1 }
let f: fn(i32) -> i32 = add_one;
println!("{}", f(5)); // 6

// Rust Option 2: impl Fn — monomorphized, zero overhead, can capture
+// Rust 选项 2:impl Fn —— 单态化、零开销、可以捕获
fn apply(val: i32, f: impl Fn(i32) -> i32) -> i32 { f(val) }
let n = 10;
- let result = apply(5, |x| x + n);  // Closure captures `n`
+ let result = apply(5, |x| x + n);  // Capture / 闭包捕获了 `n`

// Rust Option 3: Box<dyn Fn> — type-erased, heap-allocated (like std::function)
+// Rust 选项 3:Box<dyn Fn> —— 类型擦除、堆分配(类似于 std::function)
fn make_adder(n: i32) -> Box<dyn Fn(i32) -> i32> {
    Box::new(move |x| x + n)
}
let adder = make_adder(10);
println!("{}", adder(5));  // 15

// Storing heterogeneous callables (like vector<function<int(int)>>):
+// 存储异构可调用对象(如 vector<function<int(int)>>):
let callbacks: Vec<Box<dyn Fn(i32) -> i32>> = vec![
    Box::new(|x| x + 1),
    Box::new(|x| x * 2),
    Box::new(make_adder(100)),
];
for cb in &callbacks {
    println!("{}", cb(5));  // 6, 10, 105
}
}

-| When to use | C++ Equivalent | Rust Choice | +| When to use / 场景 | C++ Equivalent / C++ 等效 | Rust Choice / Rust 选择 | |————|—————|———––| -| Top-level function, no captures | Function pointer | fn(Args) -> Ret | -| Top-level / 无捕获的顶层函数 | Function pointer | fn(Args) -> Ret | -| Generic function accepting callables | Template parameter | impl Fn(Args) -> Ret (static dispatch) | -| Generic / 接收可调用对象的泛型函数 | Template parameter | impl Fn(Args) -> Ret (静态分发) | -| Trait bound in generics | template<typename F> | F: Fn(Args) -> Ret | -| Trait / 泛型中的 Trait 约束 | template<typename F> | F: Fn(Args) -> Ret | -| Stored callable, type-erased | std::function<R(Args)> | Box<dyn Fn(Args) -> Ret> | -| Stored / 类型擦除的存储对象 | std::function | Box<dyn Fn(Args) -> Ret> | -| Callback that mutates state | std::function with mutable lambda | Box<dyn FnMut(Args) -> Ret> | -| Mutates / 会修改状态的回调 | mutable lambda | Box<dyn FnMut(Args) -> Ret> | -| One-shot callback (consumed) | std::function (moved) | Box<dyn FnOnce(Args) -> Ret> | -| One-shot / 一次性回调 (被消耗) | moved function | Box<dyn FnOnce(Args) -> Ret> |

  • Performance note: impl Fn has zero overhead (monomorphized, like a C++ template).

  • 性能说明impl Fn 具有零开销(单态化,类似于 C++ 模板)。

  • Box<dyn Fn> has the same overhead as std::function (vtable + heap allocation).

  • Box<dyn Fn> 具有与 std::function 相同的开销(虚函数表 + 堆分配)。

  • Prefer impl Fn unless you need to store heterogeneous callables.

  • 除非你需要存储异构的可调用对象,否则请优先选用 impl Fn


-| C++ STL Container | Rust Equivalent | Notes | +| C++ STL Container / C++ 容器 | Rust Equivalent / Rust 等效 | Notes / 说明 | |——————|––––––––|—––| -| std::vector<T> | Vec<T> | Nearly identical API. Rust checks bounds by default | -| vector<T> | Vec<T> | 几乎一致的 API。Rust 默认检查边界 | -| std::array<T, N> | [T; N] | Stack-allocated fixed-size array | -| array<T, N> | [T; N] | 栈分配的固定大小数组 | -| std::deque<T> | std::collections::VecDeque<T> | Ring buffer. Efficient push/pop at both ends | -| deque<T> | VecDeque<T> | 环形缓冲区。两端高效 push/pop | -| std::list<T> | std::collections::LinkedList<T> | Rarely used in Rust — Vec is almost always faster | -| list<T> | LinkedList<T> | Rust 中极少使用 —— Vec 几乎总是更快 | -| std::forward_list<T> | No equivalent | Use Vec or VecDeque | -| forward_list<T> | 无直接对应 | 使用 VecVecDeque | -| std::unordered_map<K, V> | std::collections::HashMap<K, V> | Uses SipHash by default (DoS-resistant) | -| unordered_map<K, V> | HashMap<K, V> | 默认使用 SipHash(抗 DoS 攻击) | -| std::map<K, V> | std::collections::BTreeMap<K, V> | B-tree; keys sorted; K: Ord required | -| map<K, V> | BTreeMap<K, V> | B 树;键有序;要求 K: Ord | -| std::unordered_set<T> | std::collections::HashSet<T> | T: Hash + Eq required | -| unordered_set<T> | HashSet<T> | 要求 T: Hash + Eq | -| std::set<T> | std::collections::BTreeSet<T> | Sorted set; T: Ord required | -| set<T> | BTreeSet<T> | 有序集合;要求 T: Ord | -| std::priority_queue<T> | std::collections::BinaryHeap<T> | Max-heap by default (same as C++) | -| priority_queue<T> | BinaryHeap<T> | 默认大顶堆(与 C++ 一致) | -| std::stack<T> | Vec<T> with .push() / .pop() | No separate stack type needed | -| stack<T> | Vec<T> | 无需单独的栈类型 | -| std::queue<T> | VecDeque<T> with .push_back() / .pop_front() | No separate queue type needed | -| queue<T> | VecDeque<T> | 无需单独的队列类型 | -| std::string | String | UTF-8 guaranteed, not null-terminated | -| string | String | 保证 UTF-8,不以 null 结尾 | -| std::string_view | &str | Borrowed UTF-8 slice | -| string_view | &str | 借用的 UTF-8 切片 | -| std::span<T> (C++20) | &[T] / &mut [T] | Rust slices have been a first-class type since 1.0 | -| span<T> (C++20) | &[T] / &mut [T] | Rust 切片自 1.0 起就是一等公民类型 | -| std::tuple<A, B, C> | (A, B, C) | First-class syntax, destructurable | -| tuple<A, B, C> | (A, B, C) | 一等公民语法,支持解构 | -| std::pair<A, B> | (A, B) | Just a 2-element tuple | -| pair | 二元元组 | 仅为两个元素的元组 | -| std::bitset<N> | No std equivalent | Use the bitvec crate or [u8; N/8] | -| bitset<N> | 标准库无对应 | 使用 bitvec crate 或 [u8; N/8] |

  • Key differences:
  • 主要差异
    • Rust’s HashMap/HashSet require K: Hash + Eq — the compiler enforces this at the type level, unlike C++ where using an unhashable key gives a template error deep in the STL
    • Rust 的 HashMap/HashSet 要求 K: Hash + Eq —— 编译器在类型层面强制执行此要求,而不像 C++ 那样在 STL 深处报错。
    • Vec indexing (v[i]) panics on out-of-bounds by default. Use .get(i) for Option<&T> or iterators to avoid bounds checks entirely
    • Vec 索引(v[i])默认在越界时触发 panic。使用 .get(i) 获取 Option<&T> 或使用迭代器以完全避免边界检查。
    • No std::multimap or std::multiset — use HashMap<K, Vec<V>> or BTreeMap<K, Vec<V>>
    • 没有 std::multimapstd::multiset —— 请使用 HashMap<K, Vec<V>>BTreeMap<K, Vec<V>>

  • C++ defines three levels of exception safety (Abrahams guarantees):
  • C++ 定义了三个级别的异常安全性(Abrahams 保证):

-| C++ Level | Meaning | Rust Equivalent | +| C++ Level / C++ 级别 | Meaning / 含义 | Rust Equivalent / Rust 等效 | |–––––|———|––––––––| -| No-throw | Function never throws | Function never panics (returns Result) | -| No-throw | 绝不抛出异常 | 绝不发生 panic (返回 Result) | -| Strong (commit-or-rollback) | If it throws, state is unchanged | Ownership model makes this natural — if ? returns early, partially built values are dropped | -| Strong (提交或回滚) | 发生异常时状态不变 | 所有权模型使其变得自然 —— ? 提前返回时部分构建的值会被 Drop | -| Basic | If it throws, invariants are preserved | Rust’s default — Drop runs, no leaks | -| Basic | 发生异常时保持逻辑一致 | Rust 默认行为 —— Drop 执行,无泄漏 |

#![allow(unused)]
fn main() {
// Strong guarantee for free — if file.write() fails, config is unchanged
+// 免费获得强力保证 —— 如果 file.write() 失败,config 保持不变
fn update_config(config: &mut Config, path: &str) -> Result<(), Error> {
-    let new_data = fetch_from_network()?; // Err → early return, config untouched
+    let new_data = fetch_from_network()?; // Err -> early / 错误则提前返回,config 未受影响
-    let validated = validate(new_data)?;   // Err → early return, config untouched
+    let validated = validate(new_data)?;   // Err -> early / 错误则提前返回,config 未受影响
-    *config = validated;                   // Only reached on success (commit)
+    *config = validated;                   // Commit / 仅在成功时到达此处(提交)
    Ok(())
}
}
  • In C++, achieving the strong guarantee requires manual rollback or the copy-and-swap
  • 在 C++ 中,实现强力保证需要手动回滚或使用 copy-and-swap
  • idiom. In Rust, ? propagation gives you the strong guarantee by default for most code.
  • 惯用法。在 Rust 中,通过 ? 的传播,大多数代码默认就能获得强力保证。
#![allow(unused)]
fn main() {
use std::panic;

// Catch a panic (like catch(...) in C++) — rarely needed
+// 捕获一个 panic(类似于 C++ 中的 catch(...))—— 极少需要
let result = panic::catch_unwind(|| {
-    // Code that might panic
+    // Might panic / 可能引发 panic 的代码
    let v = vec![1, 2, 3];
-    v[10]  // Panics! (index out of bounds)
+    v[10]  // Panics! / 触发 Panic!(越界索引)
});

match result {
    Ok(val) => println!("Got: {val}"),
-    Err(_) => eprintln!("Caught a panic — cleaned up"),
+    Err(_) => eprintln!("Caught / 捕获了 panic —— 已清理"),
}
}
#![allow(unused)]
fn main() {
use std::panic::UnwindSafe;

// Types behind &mut are NOT UnwindSafe by default — the panic may have
+// 默认情况下 &mut 背后的类型不是 UnwindSafe 的 —— 因为 panic 可能
// left them in a partially-modified state
+// 导致它们处于被部分修改的状态
fn safe_execute<F: FnOnce() + UnwindSafe>(f: F) {
    let _ = std::panic::catch_unwind(f);
}

// Use AssertUnwindSafe to override when you've audited the code:
+// 当你审核过代码后,使用 AssertUnwindSafe 进行覆盖:
use std::panic::AssertUnwindSafe;
let mut data = vec![1, 2, 3];
let _ = std::panic::catch_unwind(AssertUnwindSafe(|| {
    data.push(4);
}));
}

-| C++ Exception Pattern | Rust Equivalent | +| C++ Exception Pattern / C++ 异常模式 | Rust Equivalent / Rust 等效 | |———————–|—————–| -| throw MyException() | return Err(MyError::...) (preferred) or panic!("...") | -| throw | return Err (推荐) 或 panic! | -| try { } catch (const E& e) | match result { Ok(v) => ..., Err(e) => ... } or ? | -| try / catch | match result? | -| catch (...) | std::panic::catch_unwind(...) | -| catch (...) | std::panic::catch_unwind(...) | -| noexcept | -> Result<T, E> (errors are values, not exceptions) | -| noexcept | -> Result<T, E> (错误是值,而非异常) | -| RAII cleanup in stack unwinding | Drop::drop() runs during panic unwinding | -| 栈展开中的 RAII 清理 | panic 展开期间执行 Drop::drop() | -| std::uncaught_exceptions() | std::thread::panicking() | -| uncaught_exceptions() | std::thread::panicking() | -| -fno-exceptions compile flag | panic = "abort" in Cargo.toml [profile] | -| -fno-exceptions 标志 | Cargo.toml 配置 panic = "abort" |

  • Bottom line: In Rust, most code uses Result<T, E> instead of exceptions,

  • 底线:在 Rust 中,大多数代码使用 Result<T, E> 而非异常,

  • making error paths explicit and composable. panic! is reserved for bugs

  • 这使得错误路径变得显式且可组合。panic! 仅保留给程序 Bug

  • (like assert! failures), not routine errors. This means “exception safety”

  • (如 assert! 失败),而非常规错误。这意味着“异常安全性”

  • is largely a non-issue — the ownership system handles cleanup automatically.

  • 在很大程度上不再是一个问题 —— 所有权系统会自动处理清理工作。


-| C++ Pattern | Rust Idiom | Notes | +| C++ Pattern / C++ 模式 | Rust Idiom / Rust 惯用法 | Notes / 说明 | |––––––––|—————|–––––| -| class Derived : public Base | enum Variant { A {...}, B {...} } | Prefer enums for closed sets | -| 继承体系 | enum Variant { ... } | 封闭集合首选枚举 | -| virtual void method() = 0 | trait MyTrait { fn method(&self); } | Use for open/extensible interfaces | -| 纯虚函数 | trait MyTrait { ... } | 用于开放/可扩展接口 | -| dynamic_cast<Derived*>(ptr) | match value { Variant::A(data) => ..., } | Exhaustive, no runtime failure | -| dynamic_cast | match / 模式匹配 | 穷尽性检查,无运行时失败 | -| vector<unique_ptr<Base>> | Vec<Box<dyn Trait>> | Only when genuinely polymorphic | -| unique_ptr 容器 | Vec<Box<dyn Trait>> | 仅在确实需要多态时使用 | -| shared_ptr<T> | Rc<T> or Arc<T> | Prefer Box<T> or owned values first | -| shared_ptr<T> | Rc<T> / Arc<T> | 优先使用 Box 或拥有所有权的值 | -| enable_shared_from_this<T> | Arena pattern (Vec<T> + indices) | Eliminates reference cycles entirely | -| shared_from_this | Arena 模式 / 索引 | 完全消除引用循环 | -| Base* m_pFramework in every class | fn execute(&mut self, ctx: &mut Context) | Pass context, don’t store pointers | -| 持有父级指针 | 传递 Context 借用 | 传递上下文,不要存储指针 | -| try { } catch (...) { } | match result { Ok(v) => ..., Err(e) => ... } | Or use ? for propagation | -| try / catch | match result | 或使用 ? 进行传播 | -| std::optional<T> | Option<T> | match required, can’t forget None | -| optional<T> | Option<T> | 强制匹配,不会忘记 None 场景 | -| const std::string& parameter | &str parameter | Accepts both String and &str | -| const string& 参数 | &str 参数 | 同时支持 String&str | -| enum class Foo { A, B, C } | enum Foo { A, B, C } | Rust enums can also carry data | -| enum class | enum | Rust 枚举还可以携带数据 | -| auto x = std::move(obj) | let x = obj; | Move is the default, no std::move needed | -| std::move | let x = obj; | 默认即移动,无需 std::move | -| CMake + make + lint | cargo build / test / clippy / fmt | One tool for everything | +| CMake + lint | cargo 工具链 | 全能的一站式工具 |

    1. Start with data types: Translate structs and enums first — this forces you to think about ownership
    1. 从数据类型开始:首先翻译结构体和枚举 —— 这会强制你思考所有权问题。
    1. Convert factories to enums: If a factory creates different derived types, it should probably be enum + match
    1. 将工厂转为枚举:如果一个工厂创建不同的派生类型,它通常应该是一个 enum + match 结构。
    1. Convert god objects to composed structs: Group related fields into focused structs
    1. 将上帝对象转为组合结构体:将相关的字段分组到专门的结构体中。
    1. Replace pointers with borrows: Convert Base* stored pointers to &'a T lifetime-bounded borrows
    1. 用借用替换指针:将存储的 Base* 指针转换为受生命周期限制的 &'a T 借用。
    1. Use Box<dyn Trait> sparingly: Only for plugin systems and test mocking
    1. 慎用 Box<dyn Trait>:仅将其用于插件系统和测试 Mock。
    1. Let the compiler guide you: Rust’s error messages are excellent — read them carefully
    1. 让编译器引导你:Rust 的错误消息非常出色 —— 请仔细阅读它们。

19. Rust Macros: From Preprocessor to Metaprogramming / 19. Rust 宏:从预处理器到元编程

Rust Macros: From Preprocessor to Metaprogramming / Rust 宏:从预处理器到元编程

What you’ll learn / 你将学到: How Rust macros work, when to use them instead of functions or generics, and how they replace the C/C++ preprocessor. By the end of this chapter you can write your own macro_rules! macros and understand what #[derive(Debug)] does under the hood.

Rust 宏的工作原理、何时使用它们而非函数或泛型,以及它们如何替代 C/C++ 预处理器。在本章结束时,你将能编写自己的 macro_rules! 宏,并理解 #[derive(Debug)] 底层的运作机制。

  • Macros are one of the first things you encounter in Rust (println!("hello") on line one) but one of the last things most courses explain. This chapter fixes that.
  • 宏是你最早接触到的 Rust 特性之一(第一行代码通常就是 println!("hello")),但却是大多数课程最后才解释的内容。本章将填补这一空白。
  • Functions and generics handle most code reuse in Rust. Macros fill the gaps where the type system can’t reach:
  • 在 Rust 中,函数和泛型处理了大部分代码复用。宏则填补了类型系统无法触及的空白:

-| Need | Function/Generic? | Macro? | Why | +| Need / 需求 | Function/Generic? / 函数/泛型? | Macro? / 宏? | Why / 原因 | |——|—————––|––––|—–| -| Compute a value | ✅ fn max<T: Ord>(a: T, b: T) -> T | — | Type system handles it | -| Compute value / 计算值 | ✅ fn max<T: Ord>(...) | — | 类型系统足以处理 | -| Accept variable number of arguments | ❌ Rust has no variadic functions | ✅ println!("{} {}", a, b) | Macros accept any number of tokens | -| Variadic args / 变长参数 | ❌ Rust 无变长参数函数 | ✅ println! | 宏可以接收任意数量的 Token | -| Generate repetitive impl blocks | ❌ No way with generics alone | ✅ macro_rules! | Macros generate code at compile time | -| 重复的 impl 块 | ❌ 仅靠泛型无法实现 | ✅ macro_rules! | 宏在编译时生成代码 | -| Run code at compile time | ❌ const fn is limited | ✅ Procedural macros | Full Rust code runs at compile time | -| 编译时执行代码 | ❌ const fn 限制较多 | ✅ 过程宏 | 完整的 Rust 代码可在编译时运行 | -| Conditionally include code | ❌ | ✅ #[cfg(...)] | Attribute macros control compilation | -| 条件包含代码 | ❌ | ✅ cfg 属性 | 属性宏控制编译过程 |

  • If you’re coming from C/C++, think of macros as the only correct replacement for the preprocessor — except they operate on the syntax tree instead of raw text, so they’re hygienic (no accidental name collisions) and type-aware.
  • 如果你来自 C/C++ 领域,可以将宏视为预处理器的唯一正确替代方案 —— 不同之处在于它们操作的是语法树而非原始文本,因此它们是“卫生”的(不会发生意外的名称冲突)且能感知类型。
  • For C developers: Rust macros replace #define entirely. There is no textual preprocessor. See ch18 for the full preprocessor → Rust mapping.

  • 致 C 开发者:Rust 宏完全替代了 #define。Rust 中没有文本预处理器。有关预处理器到 Rust 的完整映射,请参阅第 18 章。


  • Declarative macros (also called “macros by example”) are Rust’s most common macro form. They use pattern matching on syntax, similar to match on values.
  • 声明式宏(也称为“示例宏”)是 Rust 中最常见的宏形式。它们在语法上使用模式匹配,类似于对值进行的 match 操作。
macro_rules! say_hello {
    () => {
-        println!("Hello!");
+        println!("Hello!"); // 打印
    };
}

fn main() {
-    say_hello!();  // Expands to: println!("Hello!");
+    say_hello!();  // Expands / 展开为:println!("Hello!");
}
  • The ! after the name is what tells you (and the compiler) this is a macro invocation.
  • 名称后的 ! 告诉开发者(以及编译器)这是一个宏调用。
  • Macros match on token trees using fragment specifiers:
  • 宏使用片段指示符(Fragment Specifiers)对“Token 树”进行匹配:
macro_rules! greet {
-    // Pattern 1: no arguments
+    // Pattern 1 / 模式 1:无参数
    () => {
        println!("Hello, world!");
    };
-    // Pattern 2: one expression argument
+    // Pattern 2 / 模式 2:一个表达式参数
    ($name:expr) => {
        println!("Hello, {}!", $name);
    };
}

fn main() {
    greet!();           // "Hello, world!"
    greet!("Rust");     // "Hello, Rust!"
}

-| Specifier | Matches | Example | +| Specifier / 指示符 | Matches / 匹配项 | Example / 示例 | |———–|———|———| -| $x:expr | Any expression | 42, a + b, foo() | -| $x:expr | 任意表达式 | 42, a + b | -| $x:ty | A type | i32, Vec<String>, &str | -| $x:ty | 类型 | i32, &str | -| $x:ident | An identifier | foo, my_var | -| $x:ident | 标识符 | foo, my_var | -| $x:pat | A pattern | Some(x), _, (a, b) | -| $x:pat | 模式 | Some(x), _ | -| $x:stmt | A statement | let x = 5; | -| $x:stmt | 语句 | let x = 5; | -| $x:block | A block | { println!("hi"); 42 } | -| $x:block | 代码块 | { ... } | -| $x:literal | A literal | 42, "hello", true | -| $x:literal | 字面量 | 42, "hello" | -| $x:tt | A single token tree | Anything — the wildcard | -| $x:tt | 单个 Token 树 | 任意内容 —— 通配符 | -| $x:item | An item (fn, struct, impl, etc.) | fn foo() {} | -| $x:item | 项 (函数、结构体等) | fn foo() {} |

  • C/C++ macros can’t loop. Rust macros can repeat patterns:
  • C/C++ 宏无法循环。Rust 宏则可以重复模式:
macro_rules! make_vec {
-    // Match zero or more comma-separated expressions
+    // Match zero / 匹配零个或多个逗号分隔的表达式
    ( $( $element:expr ),* ) => {
        {
            let mut v = Vec::new();
-            $( v.push($element); )*  // Repeat for each matched element
+            $( v.push($element); )*  // Repeat / 为每个匹配的元素重复执行 push
            v
        }
    };
}

fn main() {
    let v = make_vec![1, 2, 3, 4, 5];
    println!("{v:?}");  // [1, 2, 3, 4, 5]
}
  • The $( ... ),* syntax means “match zero or more of this pattern, separated by commas.” The $( ... )* in the expansion repeats the body once for each match.
  • $( ... ),* 语法表示“匹配零个或多个此模式,以逗号分隔”。展开过程中的 $( ... )* 则会为每个匹配项重复执行其内部的代码体。
  • This is exactly how vec![] is implemented in the standard library. The actual source is:

  • 标准库中的 vec![] 正是以这种方式实现的。其源代码大致如下:

  • macro_rules! vec {

  • () => { Vec::new() };
    
  • ($elem:expr; $n:expr) => { vec::from_elem($elem, $n) };
    
  • ($($x:expr),+ $(,)?) => { <[_]>::into_vec(Box::new([$($x),+])) };
    
  • }

  • macro_rules! vec {

  • () => { Vec::new() };
    
  • ($elem:expr; $n:expr) => { vec::from_elem($elem, $n) };
    
  • ($($x:expr),+ $(,)?) => { <[_]>::into_vec(Box::new([$($x),+])) };
    
  • }

  • The $(,)? at the end allows an optional trailing comma.

  • 末尾的 $(,)? 允许可选的尾随逗号。

-| Operator | Meaning | Example | +| Operator / 操作符 | Meaning / 含义 | Example / 示例 | |–––––|———|———| -| $( ... )* | Zero or more | vec![], vec![1], vec![1, 2, 3] | -| $( ... )* | 零个或多个 | vec![], vec![1, 2] | -| $( ... )+ | One or more | At least one element required | -| $( ... )+ | 一个或多个 | 至少需要一个元素 | -| $( ... )? | Zero or one | Optional element | -| $( ... )? | 零个或一个 | 可选元素 |

  • The standard library has vec![] but no hashmap!{}. Let’s build one:
  • 标准库提供了 vec![] 但没有提供 hashmap!{}。让我们来构建一个:
macro_rules! hashmap {
    ( $( $key:expr => $value:expr ),* $(,)? ) => {
        {
            let mut map = std::collections::HashMap::new();
            $( map.insert($key, $value); )*
            map
        }
    };
}

fn main() {
    let scores = hashmap! {
        "Alice" => 95,
        "Bob" => 87,
-        "Carol" => 92,  // trailing comma OK thanks to $(,)?
+        "Carol" => 92,  // Trailing comma / 多亏了 $(,)?,尾随逗号也是允许的
    };
    println!("{scores:?}");
}
  • A pattern common in embedded/diagnostic code — check a condition and return an error:
  • 嵌入式/诊断代码中的常见模式 —— 检查一个条件并返回错误:
#![allow(unused)]
fn main() {
use thiserror::Error;

#[derive(Error, Debug)]
enum DiagError {
    #[error("Check failed: {0}")]
    CheckFailed(String),
}

macro_rules! diag_check {
    ($cond:expr, $msg:expr) => {
        if !($cond) {
            return Err(DiagError::CheckFailed($msg.to_string()));
        }
    };
}

fn run_diagnostics(temp: f64, voltage: f64) -> Result<(), DiagError> {
    diag_check!(temp < 85.0, "GPU too hot");
    diag_check!(voltage > 0.8, "Rail voltage too low");
    diag_check!(voltage < 1.5, "Rail voltage too high");
    println!("All checks passed");
    Ok(())
}
}
  • C/C++ comparison:

  • C/C++ 对比:

  • // C preprocessor — textual substitution, no type safety, no hygiene

  • #define DIAG_CHECK(cond, msg) \

  • do { if (!(cond)) { log_error(msg); return -1; } } while(0)
    
  • // C 预处理器 — 文本替换,无类型安全,不卫生

  • #define DIAG_CHECK(cond, msg) \

  • do { if (!(cond)) { log_error(msg); return -1; } } while(0)
    
  • The Rust version returns a proper Result type, has no double-evaluation risk, and the compiler checks that $cond is actually a bool expression.

  • Rust 版本返回正规的 Result 类型,没有重复求值的风险,而且编译器会检查 $cond 是否确实是 bool 表达式。

  • C/C++ macro bugs often come from name collisions:
  • C/C++ 宏的 Bug 通常源于名称冲突:
- // C: dangerous — `x` could shadow the caller's `x`
+ // C: 危险 — `x` 可能会遮蔽调用者的 `x`
#define SQUARE(x) ((x) * (x))
int x = 5;
- int result = SQUARE(x++);  // UB: x incremented twice!
+ int result = SQUARE(x++);  // UB / 未定义行为:x 被递增了两次!
  • Rust macros are hygienic — variables created inside a macro don’t leak out:
  • Rust 宏是**卫生(Hygienic)**的 —— 在宏内部创建的变量不会泄露到外部:
macro_rules! make_x {
    () => {
-        let x = 42;  // This `x` is scoped to the macro expansion
+        let x = 42;  // 此 `x` 的作用域仅限于宏展开内部
    };
}

fn main() {
    let x = 10;
    make_x!();
-    println!("{x}");  // Prints 10, not 42 — hygiene prevents collision
+    println!("{x}");  // Prints / 打印 10,而非 42 —— 卫生性防止了冲突
}
  • The macro’s x and the caller’s x are treated as different variables by the compiler, even though they have the same name. This is impossible with the C preprocessor.
  • 编译器将宏内部的 x 和调用者的 x 视为不同的变量,即使它们同名。这在 C 预处理器中是不可能实现的。

  • You’ve been using these since chapter 1 — here’s what they actually do:
  • 自第 1 章起你就一直在使用这些宏 —— 以下是它们的实际作用:

-| Macro | What it does | Expands to (simplified) | +| Macro / 宏 | What it does / 作用 | Expands to / 展开为 (简化版) | |—––|———––|————————| -| println!("{}", x) | Format and print to stdout + newline | std::io::_print(format_args!(...)) | -| println! | 格式化并打印到 stdout | io::_print(...) | -| eprintln!("{}", x) | Print to stderr + newline | Same but to stderr | -| eprintln! | 打印到 stderr | 打印到 stderr | -| format!("{}", x) | Format into a String | Allocates and returns a String | -| format! | 格式化为 String | 分配并返回 String | -| vec![1, 2, 3] | Create a Vec with elements | Vec::from([1, 2, 3]) (approximately) | -| vec! | 创建带初始值的 Vec | Vec::from(...) | -| todo!() | Mark unfinished code | panic!("not yet implemented") | -| todo! | 标记未完成的代码 | panic! 带有待办消息 | -| unimplemented!() | Mark deliberately unimplemented code | panic!("not implemented") | -| unreachable!() | Mark code the compiler can’t prove unreachable | panic!("unreachable") | -| assert!(cond) | Panic if condition is false | if !cond { panic!(...) } | -| assert_eq!(a, b) | Panic if values aren’t equal | Shows both values on failure | -| dbg!(expr) | Print expression + value to stderr, return value | eprintln!("[file:line] expr = {:#?}", &expr); expr | -| dbg! | 打印表达式及值,并返回该值 | eprintln! + 值的转发 | -| include_str!("file.txt") | Embed file contents as &str at compile time | Reads file during compilation | -| include_str! | 编译时以 &str 嵌入文件内容 | 编译阶段读取文件 | -| include_bytes!("data.bin") | Embed file contents as &[u8] at compile time | Reads file during compilation | -| cfg!(condition) | Compile-time condition as a bool | true or false based on target | -| env!("VAR") | Read environment variable at compile time | Fails compilation if not set | -| env! | 编译时读取环境变量 | 失败则导致编译报错 | -| concat!("a", "b") | Concatenate literals at compile time | "ab" |

fn factorial(n: u32) -> u32 {
-    if dbg!(n <= 1) {     // Prints: [src/main.rs:2] n <= 1 = false
+    if dbg!(n <= 1) {     // Prints / 打印:[src/main.rs:2] n <= 1 = false
-        dbg!(1)           // Prints: [src/main.rs:3] 1 = 1
+        dbg!(1)           // Prints / 打印:[src/main.rs:3] 1 = 1
    } else {
-        dbg!(n * factorial(n - 1))  // Prints intermediate values
+        dbg!(n * factorial(n - 1))  // Prints / 打印中间值
    }
}

fn main() {
-    dbg!(factorial(4));   // Prints all recursive calls with file:line
+    dbg!(factorial(4));   // Prints / 打印所有带文件名和行号的递归调用
}
  • dbg! returns the value it wraps, so you can insert it anywhere without changing program behavior. It prints to stderr (not stdout), so it doesn’t interfere with program output. Remove all dbg! calls before committing code.
  • dbg! 会返回它包裹的值,因此你可以将其插入任何地方而不影响程序行为。它打印到 stderr(而非 stdout),因此不会干扰程序输出。在提交代码前,请删除所有的 dbg! 调用。
  • Since println!, format!, eprintln!, and write! all use the same format machinery, here’s the quick reference:
  • 由于 println!format!eprintln!write! 通用一套格式化机制,以下是快速参考:
#![allow(unused)]
fn main() {
let name = "sensor";
let value = 3.14159;
let count = 42;

- println!("{name}");                    // Variable by name (Rust 1.58+)
+ println!("{name}");                    // Variable / 按名称引用变量 (Rust 1.58+)
- println!("{}", name);                  // Positional
+ println!("{}", name);                  // Positional / 位置匹配
- println!("{value:.2}");                // 2 decimal places: "3.14"
+ println!("{value:.2}");                // 2 dec / 2 位小数:"3.14"
- println!("{count:>10}");               // Right-aligned, width 10: "        42"
+ println!("{count:>10}");               // Align / 右对齐,宽度 10:"        42"
- println!("{count:0>10}");              // Zero-padded: "0000000042"
+ println!("{count:0>10}");              // Padding / 零填充:"0000000042"
- println!("{count:#06x}");              // Hex with prefix: "0x002a"
+ println!("{count:#06x}");              // Hex / 带前缀的十六进制:"0x002a"
- println!("{count:#010b}");             // Binary with prefix: "0b00101010"
+ println!("{count:#010b}");             // Bin / 带前缀的二进制:"0b00101010"
- println!("{value:?}");                 // Debug format
+ println!("{value:?}");                 // Debug / Debug 格式化
- println!("{value:#?}");                // Pretty-printed Debug format
+ println!("{value:#?}");                // Pretty / 美化后的 Debug 格式化
}
  • For C developers: Think of this as a type-safe printf — the compiler checks that {:.2} is applied to a float, not a string. No %s/%d format mismatch bugs.

  • 致 C 开发者:将其视为类型安全的 printf —— 编译器会检查 {:.2} 是否应用于浮点数而非字符串。不会再有 %s/%d 格式不匹配的 Bug。

  • For C++ developers: This replaces std::cout << std::fixed << std::setprecision(2) << value with a single readable format string.

  • 致 C++ 开发者:这用单个可读的格式化字符串替代了繁杂的 std::cout << std::fixed << ... << value 调用。


  • You’ve seen #[derive(...)] on nearly every struct in this book:
  • 在本书的几乎每个结构体上你都见过 #[derive(...)]
#![allow(unused)]
fn main() {
#[derive(Debug, Clone, PartialEq)]
struct Point {
    x: f64,
    y: f64,
}
}
  • #[derive(Debug)] is a derive macro — a special kind of procedural macro that generates trait implementations automatically. Here’s what it produces (simplified):
  • #[derive(Debug)] 是一个派生宏 —— 一种特殊的过程宏,可以自动生成 Trait 实现。以下是它生成的代码(简化版):
#![allow(unused)]
fn main() {
- // What #[derive(Debug)] generates for Point:
+ // #[derive(Debug)] 为 Point 自动生成的代码:
impl std::fmt::Debug for Point {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Point")
            .field("x", &self.x)
            .field("y", &self.y)
            .finish()
    }
}
}
  • Without #[derive(Debug)], you’d have to write that impl block by hand for every struct.
  • 如果没有 #[derive(Debug)],你就必须为每个结构体手动编写这个 impl 块。

-| Derive | What it generates | When to use | +| Derive / 派生 | What it generates / 生成内容 | When to use / 场景 | |––––|—————––|———––| -| Debug | {:?} formatting | Almost always — enables printing for debugging | -| Debug | {:?} 格式化 | 几乎所有场景 —— 启用调试打印 | -| Clone | .clone() method | When you need to duplicate values | -| Clone | .clone() 方法 | 需要复制值时 | -| Copy | Implicit copy on assignment | Small, stack-only types (integers, [f64; 3]) | -| Copy | 隐式赋值拷贝 | 小型、仅栈类型 (整数、[f64; 3]) | -| PartialEq / Eq | == and != operators | When you need equality comparison | -| Eq | ==!= 运算符 | 需要相等性比较时 | -| PartialOrd / Ord | <, >, <=, >= operators | When you need ordering | -| Ord | 比较运算符 | 需要排序时 | -| Hash | Hashing for HashMap/HashSet keys | Types used as map keys | -| Hash | 哈希计算 | 用作 Map 的键时 | -| Default | Type::default() constructor | Types with sensible zero/empty values | -| Default | 默认值构造器 | 具有合理的“零值”或空值的类型 | -| serde::Serialize / Deserialize | JSON/TOML/etc. serialization | Data types that cross API boundaries | -| serde | 序列化/反序列化 | 涉及 API 边界的数据类型 |

- Should I derive it?
+ 我该派生它吗?
  │
-   ├── Does my type contain only types that implement the trait?
+   ├── 所有的成员类型是否都实现了该 Trait?
-   │     ├── Yes → #[derive] will work
+   │     ├── 是 → #[derive] 可以正常工作
-   │     └── No  → Write a manual impl (or skip it)
+   │     └── 否 → 需要手动编写 impl(或者放弃实现)
  │
-   └── Will users of my type reasonably expect this behavior?
+   └── 用户是否会理所当然地期望这种行为?
-         ├── Yes → Derive it (Debug, Clone, PartialEq are almost always reasonable)
+         ├── 是 → 派生它(Debug, Clone, PartialEq 几乎总是合理的)
-         └── No  → Don't derive (e.g., don't derive Copy for a type with a file handle)
+         └── 否 → 不要派生(例如,不要为包含文件句柄的类型派生 Copy)
  • C++ comparison: #[derive(Clone)] is like auto-generating a correct copy constructor. #[derive(PartialEq)] is like auto-generating operator== that compares each field — something C++20’s = default spaceship operator finally provides.

  • C++ 对比#[derive(Clone)] 类似于自动生成正确的拷贝构造函数。#[derive(PartialEq)] 类似于自动生成按字段比较的 operator== —— 这正是 C++20 的 = default 飞船操作符最终提供的功能。


  • Attribute macros transform the item they’re attached to. You’ve already used several:
  • 属性宏会转换它们所附加的项。你已经使用过好几个了:
#![allow(unused)]
fn main() {
- #[test]                    // Marks a function as a test
+ #[test]                    // Marks / 将函数标记为测试
fn test_addition() {
    assert_eq!(2 + 2, 4);
}

- #[cfg(target_os = "linux")] // Conditionally includes this function
+ #[cfg(target_os = "linux")] // Conditional / 条件包含该函数
fn linux_only() { /* ... */ }

- #[derive(Debug)]            // Generates Debug implementation
+ #[derive(Debug)]            // Generates / 生成 Debug 实现
struct MyType { /* ... */ }

- #[allow(dead_code)]         // Suppresses a compiler warning
+ #[allow(dead_code)]         // Suppress / 抑制编译器警告
fn unused_helper() { /* ... */ }

- #[must_use]                 // Warn if return value is discarded
+ #[must_use]                 // Warn / 如果忽略返回值则发出警告
fn compute_checksum(data: &[u8]) -> u32 { /* ... */ }
}
  • Common built-in attributes:
  • 常见的内置属性:

-| Attribute | Purpose | +| Attribute / 属性 | Purpose / 用途 | |———–|———| -| #[test] | Mark as test function | -| #[test] | 标记为测试函数 | -| #[cfg(...)] | Conditional compilation | -| #[cfg(...)] | 条件编译 | -| #[derive(...)] | Auto-generate trait impls | -| #[derive(...)] | 自动生成 Trait 实现 | -| #[allow(...)] / #[deny(...)] / #[warn(...)] | Control lint levels | -| Lint control | 控制代码检查级别 | -| #[must_use] | Warn on unused return values | -| #[must_use] | 警告未使用的返回值 | -| #[inline] / #[inline(always)] | Hint to inline the function | -| #[inline] | 提示函数内联 | -| #[repr(C)] | Use C-compatible memory layout (for FFI) | -| #[repr(C)] | 使用 C 兼容的内存布局 (用于 FFI) | -| #[no_mangle] | Don’t mangle the symbol name (for FFI) | -| #[no_mangle] | 禁止符号修饰 (用于 FFI) | -| #[deprecated] | Mark as deprecated with optional message | -| #[deprecated] | 标记为已废弃 |

  • For C/C++ developers: Attributes replace a mix of preprocessor directives (#pragma, __attribute__((...))), and compiler-specific extensions. They’re part of the language grammar, not bolted-on extensions.

  • 致 C/C++ 开发者:属性替代了预处理器指令(#pragma, __attribute__((...)))和编译器特定扩展。它们是语言语法的一部分,而不是额外的修补插件。


  • Procedural macros (“proc macros”) are macros written as separate Rust programs that run at compile time and generate code. They’re more powerful than macro_rules! but also more complex.
  • 过程宏(“Proc Macros”)是作为独立的 Rust 程序编写的宏,它们在编译时运行并生成代码。它们比 macro_rules! 更强大,但也更复杂。
  • There are three kinds:
  • 共有三种类型:

-| Kind | Syntax | Example | What it does | +| Kind / 类型 | Syntax / 语法 | Example / 示例 | What it does / 作用 | |——|––––|———|———––| -| Function-like | my_macro!(...) | sql!(SELECT * FROM users) | Parses custom syntax, generates Rust code | -| Function-like | my_macro!(...) | sql!(...) | 解析自定义语法,生成 Rust 代码 | -| Derive | #[derive(MyTrait)] | #[derive(Serialize)] | Generates trait impl from struct definition | -| Derive | #[derive(...)] | #[derive(Serialize)] | 从结构体定义生成 Trait 实现 | -| Attribute | #[my_attr] | #[tokio::main], #[instrument] | Transforms the annotated item | -| Attribute | #[my_attr] | #[tokio::main] | 转换被标记的项目 |

    • #[derive(Error)] from thiserror — generates Display and From impls for error enums
    • 来自 thiserror#[derive(Error)] —— 为错误枚举生成 DisplayFrom 实现。
    • #[derive(Serialize, Deserialize)] from serde — generates serialization code
    • 来自 serde#[derive(Serialize, Deserialize)] —— 生成序列化代码。
    • #[tokio::main] — transforms async fn main() into a runtime setup + block_on
    • #[tokio::main] —— 将 async fn main() 转换为运行时设置及 block_on 调用。
    • #[test] — registered by the test harness (built-in proc macro)
    • #[test] —— 由测试框架注册(内置的过程宏)。
  • You likely won’t need to write proc macros during this course. They’re useful when:
  • 在本课程中你可能不需要编写过程宏。它们在以下场景中非常有用:
    • You need to inspect struct fields/enum variants at compile time (derive macros)
    • 你需要在编译时检查结构体字段/枚举变体(派生宏)。
    • You’re building a domain-specific language (function-like macros)
    • 你正在构建领域特定语言(函数式宏)。
    • You need to transform function signatures (attribute macros)
    • 你需要转换函数签名(属性宏)。
  • For most code, macro_rules! or plain functions are sufficient.
  • 对于大多数代码,macro_rules! 或普通的函数就足够了。
  • C++ comparison: Procedural macros fill the role that code generators, template metaprogramming, and external tools like protoc fill in C++. The difference is that proc macros are part of the cargo build pipeline — no external build steps, no CMake custom commands.

  • C++ 对比:过程宏填补了 C++ 中代码生成器、模板元编程以及像 protoc 这样的外部工具所扮演的角色。不同之处在于过程宏是 Cargo 构建流水线的一部分 —— 无需外部构建步骤,也无需 CMake 自定义命令。


- Need to generate code?
+ 是否需要生成代码?
  │
-   ├── No → Use a function or generic function
+   ├── 否 → 使用函数或泛型函数
-   │         (simpler, better error messages, IDE support)
+   │         (更简单、错误消息更好、IDE 支持更佳)
  │
-   └── Yes ─┬── Variable number of arguments?
+   └── 是 ─┬── 是否需要变长参数?
-             │     └── Yes → macro_rules! (e.g., println!, vec!)
+             │     └── 是 → macro_rules! (例如:println!, vec!)
-             │
-             ├── Repetitive impl blocks for many types?
+             │
+             ├── 是否需要为多种类型生成重复的 impl 块?
-             │     └── Yes → macro_rules! with repetition
+             │     └── 是 → 带重复模式的 macro_rules!
-             │
-             ├── Need to inspect struct fields?
+             │
+             ├── 是否需要检查结构体字段?
-             │     └── Yes → Derive macro (proc macro)
+             │     └── 是 → 派生宏 (Derive macro)
-             │
-             ├── Need custom syntax (DSL)?
+             │
+             ├── 是否需要自定义语法 (DSL)?
-             │     └── Yes → Function-like proc macro
+             │     └── 是 → 函数式过程宏
-             │
-             └── Need to transform a function/struct?
+             │
+             └── 是否需要转换函数或结构体?
-                   └── Yes → Attribute proc macro
+                   └── 是 → 属性过程宏
  • General guideline: If a function or generic can do it, don’t use a macro. Macros have worse error messages, no IDE auto-complete inside the macro body, and are harder to debug.
  • 通用准则:如果函数或泛型能做到,就不要用宏。宏的错误消息更糟,在宏体内没有 IDE 自动补全,且更难调试。

  • Write a min! macro that:
  • 编写一个 min! 宏,要求:
    • min!(a, b) returns the smaller of two values
    • min!(a, b) 返回两个值中的较小者。
    • min!(a, b, c) returns the smallest of three values
    • min!(a, b, c) 返回三个值中的最小值。
    • Works with any type that implements PartialOrd
    • 适用于任何实现了 PartialOrd 的类型。
  • Hint: You’ll need two match arms in your macro_rules!.
  • 提示:你需要在 macro_rules! 中编写两个匹配分支。
  • Solution (click to expand)
  • Solution / 解决方案(点击展开)
macro_rules! min {
    ($a:expr, $b:expr) => {
        if $a < $b { $a } else { $b }
    };
    ($a:expr, $b:expr, $c:expr) => {
        min!(min!($a, $b), $c)
    };
}

fn main() {
    println!("{}", min!(3, 7));        // 3
    println!("{}", min!(9, 2, 5));     // 2
    println!("{}", min!(1.5, 0.3));    // 0.3
}
  • Note: For production code, prefer std::cmp::min or a.min(b). This exercise demonstrates the mechanics of multi-arm macros.
  • :在生产代码中,请优先使用 std::cmp::mina.min(b)。此练习仅用于演示多分支宏的机制。
  • Without looking at the example above, write a hashmap! macro that:
  • 不看上面的例子,编写一个 hashmap! 宏,要求:
    • Creates a HashMap from key => value pairs
    • key => value 对创建 HashMap
    • Supports trailing commas
    • 支持尾随逗号。
    • Works with any hashable key type
    • 适用于任何可哈希的键类型。
  • Solution (click to expand)
  • Solution / 解决方案(点击展开)
#![allow(unused)]
fn main() {
use std::collections::HashMap;
@@ -517,14 +513,14 @@

</details>

- ### 🟡 Exercise 3: `assert_approx_eq!` for floating-point comparison
+ ### 🟡 Exercise 3: `assert_approx_eq!` for floating-point comparison / 练习 3:用于浮点比较的 `assert_approx_eq!`

- Write a macro `assert_approx_eq!(a, b, epsilon)` that panics if `|a - b| > epsilon`. This is useful for testing floating-point calculations where exact equality fails.
+ 编写一个宏 `assert_approx_eq!(a, b, epsilon)`,如果 `|a - b| > epsilon` 则触发 panic。这在测试浮点运算(精确相等往往失效)时非常有用。

- <details><summary>Solution (click to expand)</summary>
+ <details><summary>Solution / 解决方案(点击展开)</summary>

```rust
macro_rules! assert_approx_eq {
    ($a:expr, $b:expr, $eps:expr) => {
        let (a, b, eps) = ($a as f64, $b as f64, $eps as f64);
@@ -553,24 +549,24 @@

</details>

- ### 🔴 Exercise 4: `impl_display_for_enum!`
+ ### 🔴 Exercise 4: `impl_display_for_enum!` / 练习 4:`impl_display_for_enum!`

- Write a macro that generates a `Display` implementation for simple C-like enums. Given:
+ 编写一个宏,为简单的 C 风格枚举生成 `Display` 实现。给定:

```rust
impl_display_for_enum! {
    enum Color {
        Red => "red",
        Green => "green",
        Blue => "blue",
    }
}
}
  • It should generate both the enum Color { Red, Green, Blue } definition AND the impl Display for Color that maps each variant to its string.
  • 它应该同时生成 enum Color { Red, Green, Blue } 定义以及将每个变体映射到其字符串的 impl Display for Color 实现。
  • Hint: You’ll need both $( ... ),* repetition and multiple fragment specifiers.
  • 提示:你将同时需要 $( ... ),* 重复模式以及多个片段指示符。
  • Solution (click to expand)
  • Solution / 解决方案(点击展开)
#![allow(unused)]
fn main() {
use std::fmt;
@@ -608,8 +604,8 @@
    assert_eq!(format!("{}", Color::Red), "red");
    println!("All tests passed!");
}
}