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

Speaker Intro and General Approach
讲者背景与整体方式

  • Speaker intro
    讲者背景
    微软 SCHIE 团队的 Principal Firmware Architect,长期做安全、系统编程、固件、操作系统、虚拟化和 CPU / 平台架构相关工作。
  • Industry veteran with expertise in security, systems programming, CPU and platform architecture, and C++ systems
    在安全、系统编程、底层平台与 C++ 系统方向积累很深。
  • Started programming in Rust in 2017 and has loved it ever since
    从 2017 年开始使用 Rust,并长期投入其中。
  • This course is intended to be interactive
    这门课希望尽量保持互动式学习。
  • Assumption: you already know Python and its ecosystem
    默认前提是:已经熟悉 Python 及其生态。
  • Examples deliberately map Python concepts to Rust equivalents
    所有示例都会尽量把 Python 概念映射到 Rust 的对应物。
  • Clarifying questions are encouraged at any time
    遇到不清楚的地方,随时打断提问完全没问题。

The Case for Rust for Python Developers
为什么 Python 开发者值得学 Rust

What you’ll learn: Why Python developers are adopting Rust, where the real-world performance gains come from, when Rust is the right choice and when Python remains the better tool, and the philosophical differences between the two languages.
本章将学习: 为什么越来越多 Python 开发者开始接触 Rust,真实世界里的性能收益来自哪里,什么时候该选 Rust、什么时候继续用 Python 更合适,以及这两门语言在设计哲学上的差异。

Difficulty: 🟢 Beginner
难度: 🟢 入门

Performance: From Minutes to Milliseconds
性能:从分钟级到毫秒级

Python is famous for developer speed, not CPU efficiency. For CPU-bound tasks, Rust often lands orders of magnitude faster while still keeping relatively high-level syntax.
Python 出名的是开发速度,不是 CPU 执行效率。对 CPU 密集任务来说,Rust 往往能快出数量级,同时语法层面又没有低到让人完全失去抽象。

# Python — ~2 seconds for 10 million calls
import time

def fibonacci(n: int) -> int:
    if n <= 1:
        return n
    a, b = 0, 1
    for _ in range(2, n + 1):
        a, b = b, a + b
    return b

start = time.perf_counter()
results = [fibonacci(n % 30) for n in range(10_000_000)]
elapsed = time.perf_counter() - start
print(f"Elapsed: {elapsed:.2f}s")  # ~2s on typical hardware
// Rust — ~0.07 seconds for the same 10 million calls
use std::time::Instant;

fn fibonacci(n: u64) -> u64 {
    if n <= 1 {
        return n;
    }
    let (mut a, mut b) = (0u64, 1u64);
    for _ in 2..=n {
        let temp = b;
        b = a + b;
        a = temp;
    }
    b
}

fn main() {
    let start = Instant::now();
    let results: Vec<u64> = (0..10_000_000).map(|n| fibonacci(n % 30)).collect();
    println!("Elapsed: {:.2?}", start.elapsed());  // ~0.07s
}

Note: Rust should be run in release mode (cargo run --release) for a fair performance comparison.
说明: 做这种性能对比时,Rust 一定要用 cargo run --release 跑,否则数字会被 debug 构建拖得很难看。

Why the difference? Python dispatches arithmetic through runtime object machinery, dictionary lookups, heap-allocated integers, and dynamic type checks. Rust compiles the same logic down to simple machine instructions.
为什么差距会这么大? 因为 Python 的算术操作要经过运行时对象系统、字典查找、堆对象拆装以及动态类型检查;Rust 则会把同样的逻辑直接编译成很朴素的机器指令。

Memory Safety Without a Garbage Collector
没有垃圾回收器的内存安全

Python’s reference-counting GC is convenient, but it also brings circular references, __del__ timing uncertainty, and memory fragmentation issues. Rust chooses a different route: ownership and compile-time guarantees.
Python 的引用计数加垃圾回收确实省心,但它也会带来循环引用、__del__ 时机不稳定、内存碎片等问题。Rust 走的是另一条路:所有权加编译期约束。

# Python — circular reference
class Node:
    def __init__(self, value):
        self.value = value
        self.parent = None
        self.children = []

    def add_child(self, child):
        self.children.append(child)
        child.parent = self

root = Node("root")
child = Node("child")
root.add_child(child)
// Rust — ownership prevents cycles by default
struct Node {
    value: String,
    children: Vec<Node>,
}

impl Node {
    fn new(value: &str) -> Self {
        Node {
            value: value.to_string(),
            children: Vec::new(),
        }
    }

    fn add_child(&mut self, child: Node) {
        self.children.push(child);
    }
}

fn main() {
    let mut root = Node::new("root");
    let child = Node::new("child");
    root.add_child(child);
}

Key insight: Rust defaults to tree-like ownership. If a graph or shared back-reference is truly needed, the code must opt in explicitly with RcRefCellWeak or similar tools.
关键理解: Rust 默认偏向树形所有权结构。如果真的需要图结构、共享回指之类复杂关系,就必须显式引入 RcRefCellWeak 这些工具,把复杂度摆到台面上来。


Common Python Pain Points That Rust Addresses
Rust 正面解决的几类 Python 常见痛点

1. Runtime Type Errors
1. 运行时类型错误

def process_user(user_id: int, name: str) -> dict:
    return {"id": user_id, "name": name.upper()}

process_user("not-a-number", 42)
process_user(None, "Alice")
process_user(1, "Alice", extra="oops")
#![allow(unused)]
fn main() {
fn process_user(user_id: i64, name: &str) -> User {
    User {
        id: user_id,
        name: name.to_uppercase(),
    }
}

// process_user("not-a-number", 42);     // ❌ Compile error
// process_user(None, "Alice");           // ❌ Compile error

#[derive(Deserialize)]
struct UserInput {
    id: i64,
    name: String,
}

let input: UserInput = serde_json::from_str(json_str)?;
process_user(input.id, &input.name);
}

Rust’s approach is to make bad combinations literally unrepresentable in ordinary code. The compiler catches the mismatch before the process even starts.
Rust 的思路是:让错误组合在普通代码里根本写不通。类型一旦对不上,程序甚至连启动机会都没有。

2. None: The Billion Dollar Mistake
2. None:价值连城的历史大坑

def find_user(user_id: int) -> dict | None:
    users = {1: {"name": "Alice"}, 2: {"name": "Bob"}}
    return users.get(user_id)

user = find_user(999)
print(user["name"])  # 💥
#![allow(unused)]
fn main() {
fn find_user(user_id: i64) -> Option<User> {
    let users = HashMap::from([
        (1, User { name: "Alice".into() }),
        (2, User { name: "Bob".into() }),
    ]);
    users.get(&user_id).cloned()
}

match find_user(999) {
    Some(user) => println!("{}", user.name),
    None => println!("User not found"),
}

let name = find_user(999)
    .map(|u| u.name)
    .unwrap_or_else(|| "Unknown".to_string());
}

Rust does allow absence, but only through Option<T>, which forces the possibility into the type. That small shift removes an enormous class of “surprise None” failures.
Rust 当然允许“值可能不存在”,但必须通过 Option&lt;T&gt; 把这种可能性写进类型系统。这个小改动,实际上直接消掉了非常大一类“突然冒出 None” 的运行时事故。

3. The GIL: Python’s Concurrency Ceiling
3. GIL:Python 并发的天花板

import threading
import time

def cpu_work(n):
    total = 0
    for i in range(n):
        total += i * i
    return total

start = time.perf_counter()
threads = [threading.Thread(target=cpu_work, args=(10_000_000,)) for _ in range(4)]
for t in threads:
    t.start()
for t in threads:
    t.join()
elapsed = time.perf_counter() - start
print(f"4 threads: {elapsed:.2f}s")
use std::thread;

fn cpu_work(n: u64) -> u64 {
    (0..n).map(|i| i * i).sum()
}

fn main() {
    let start = std::time::Instant::now();
    let handles: Vec<_> = (0..4)
        .map(|_| thread::spawn(|| cpu_work(10_000_000)))
        .collect();

    let results: Vec<u64> = handles.into_iter()
        .map(|h| h.join().unwrap())
        .collect();

    println!("4 threads: {:.2?}", start.elapsed());
}

With Rayon: parallelism can become even simpler with par_iter(), which often feels like the “why doesn’t Python just let me do this?” moment for many learners.
如果用 Rayon:并行会变得更简单,很多人第一次看到 par_iter() 时,都会有一种“这事在 Python 里怎么就这么费劲”的直观冲击。

4. Deployment and Distribution Pain
4. 部署与分发的痛点

Python deployment often means juggling Python versions, virtual environments, wheels, system libraries, and runtime startup cost. Rust usually means shipping one binary.
Python 部署经常伴随版本、虚拟环境、wheel、系统库和运行时启动成本这些问题;Rust 的常见答案则是:交付一个二进制文件。

# Python deployment checklist:
# 1. Which Python version?
# 2. Virtual environment?
# 3. C extensions and wheels?
# 4. System dependencies?
# 5. Large Docker images?
# 6. Import-heavy startup time?
#![allow(unused)]
fn main() {
// Rust deployment:
// cargo build --release -> one binary
// copy it anywhere, no runtime required
}

When to Choose Rust Over Python
什么时候该选 Rust

Choose Rust When:
更适合选 Rust 的场景

  • Performance is critical
    性能是刚性要求
  • Correctness matters deeply
    正确性要求非常高
  • Deployment simplicity matters
    部署链路越简单越好
  • Low-level control is required
    需要更底层的控制能力
  • True parallelism is valuable
    真正的 CPU 并行有价值
  • Memory efficiency affects cost
    内存效率直接影响成本
  • Latency predictability matters
    对延迟稳定性有要求

Stay with Python When:
继续留在 Python 更划算的场景

  • Rapid prototyping dominates
    原型速度最重要
  • ML / AI ecosystem is the core value
    核心价值在 ML / AI 生态
  • The code is mostly glue and orchestration
    代码主要是胶水与编排
  • Team learning cost outweighs the gain
    学习成本明显高于收益
  • Time to market beats runtime speed
    上线速度比执行速度更重要
  • Interactive workflows dominate
    Jupyter、REPL、交互分析是主工作流

Consider Both (Hybrid with PyO3):
两者结合的场景(PyO3 混合方案)

  • Compute-heavy code in Rust
    计算热点用 Rust
  • Business orchestration in Python
    业务编排继续留给 Python
  • Incremental migration of hotspots
    从热点模块开始渐进迁移
  • Keep Python’s ecosystem, add Rust’s performance
    保留 Python 生态,同时引入 Rust 性能

Real-World Impact: Why Companies Choose Rust
真实世界里的价值:为什么公司会选 Rust

Dropbox: Storage Infrastructure
Dropbox:存储基础设施

  • Python 版本 CPU 占用和内存开销更高
    原先 Python 方案 CPU 和内存压力都比较大
  • Rust 版本带来明显性能提升和内存下降
    Rust 版本显著改善性能和内存使用

Discord: Voice / Video Backend
Discord:语音视频后端

  • Earlier stacks suffered from pause-related latency problems
    早期方案在暂停与抖动上有明显问题
  • Rust gave more stable low-latency behavior
    Rust 带来了更稳定的低延迟表现

Cloudflare: Edge Workers
Cloudflare:边缘计算

  • Rust works well with WebAssembly and predictable edge execution
    Rust 很适合 WebAssembly 和边缘执行场景

Pydantic V2
Pydantic V2

  • The public Python API stayed familiar
    对外 Python API 基本保持不变
  • The Rust core delivered dramatic validation speedups
    底层 Rust 核心把校验性能拉上去了

Why This Matters for Python Developers:
这对 Python 开发者意味着什么

  1. The skills are complementary
    1. 两门语言的能力是互补关系
  2. PyO3 makes bridging practical
    2. PyO3 让桥接落地变得现实
  3. Learning Rust clarifies where Python pays overhead
    3. 学 Rust 会反过来帮助理解 Python 的性能成本到底花在哪
  4. Systems knowledge broadens career options
    4. 系统编程能力会显著扩展技术边界
  5. Better performance often means lower infrastructure cost
    5. 性能提升通常还会带来实打实的基础设施成本下降

Language Philosophy Comparison
语言哲学对照

Python Philosophy
Python 的哲学

  • Readability counts
    可读性优先
  • Batteries included
    标准库与生态都很完整
  • Duck typing
    鸭子类型
  • Developer velocity first
    优先优化开发速度
  • Dynamic everything
    动态性很强

Rust Philosophy
Rust 的哲学

  • Performance without sacrifice
    不牺牲性能
  • Correctness first
    正确性优先
  • Explicit over implicit
    显式优于隐式
  • Ownership everywhere
    资源由所有权统一管理
  • Fearless concurrency
    并发安全尽量前移到编译期
graph LR
    subgraph PY["🐍 Python"]
        direction TB
        PY_CODE["Your Code<br/>业务代码"] --> PY_INTERP["Interpreter<br/>解释器"]
        PY_INTERP --> PY_GC["Garbage Collector<br/>垃圾回收"]
        PY_GC --> PY_GIL["GIL<br/>线程并行受限"]
        PY_GIL --> PY_OS["OS / Hardware"]
    end

    subgraph RS["🦀 Rust"]
        direction TB
        RS_CODE["Your Code<br/>业务代码"] --> RS_NONE["No runtime overhead<br/>尽量无额外运行时"]
        RS_NONE --> RS_OWN["Ownership<br/>编译期所有权约束"]
        RS_OWN --> RS_THR["Native threads<br/>原生线程并行"]
        RS_THR --> RS_OS["OS / Hardware"]
    end

    style PY_INTERP fill:#fff3e0,color:#000,stroke:#e65100
    style PY_GC fill:#fff3e0,color:#000,stroke:#e65100
    style PY_GIL fill:#ffcdd2,color:#000,stroke:#c62828
    style RS_NONE fill:#c8e6c9,color:#000,stroke:#2e7d32
    style RS_OWN fill:#c8e6c9,color:#000,stroke:#2e7d32
    style RS_THR fill:#c8e6c9,color:#000,stroke:#2e7d32

Quick Reference: Rust vs Python
速查表:Rust vs Python

Concept
概念
PythonRustKey Difference
核心差异
TypingDynamicStaticErrors move earlier
错误更早暴露
MemoryGC + refcountOwnershipDeterministic cleanup
确定性清理
None/nullNone anywhereOption<T>Absence is explicit
空值显式建模
Error handlingraise / try / exceptResult<T, E>Explicit control flow
控制流更显式
MutabilityEverything mutableImmutable by defaultMutation is opt-in
修改需要显式声明
SpeedInterpretedCompiledMuch faster execution
执行速度通常快很多
ConcurrencyGIL-limitedNo GILTrue parallelism
真并行
Dependenciespip / PoetryCargoUnified tooling
工具链更统一
Build systemMultiple toolsCargoSingle main workflow
主流程更统一
Packagingpyproject.tomlCargo.tomlBoth declarative
REPLNative REPLNo main REPLCompile-first workflow
Type hintsOptionalEnforcedTypes are executable constraints
类型是强制约束

Exercises
练习

🏋️ Exercise: Mental Model Check
🏋️ 练习:心智模型检查

Challenge: For each Python snippet below, describe what Rust would require differently. No need to write full code; just explain the constraint.
挑战:针对下面每段 Python 代码,描述 Rust 会提出什么不同要求。这里不用写完整代码,只要说清楚约束就行。

  1. x = [1, 2, 3]; y = x; x.append(4)
    1. x = [1, 2, 3]; y = x; x.append(4)
  2. data = None; print(data.upper())
    2. data = None; print(data.upper())
  3. import threading; shared = []; threading.Thread(target=shared.append, args=(1,)).start()
    3. import threading; shared = []; threading.Thread(target=shared.append, args=(1,)).start()
🔑 Solution
🔑 参考答案
  1. Ownership move: let y = x; would move ownership, so trying to mutate x afterward would fail to compile unless the design uses borrowing or cloning.
    1. 所有权移动:如果写成 let y = x;,所有权会转移给 y,之后再改 x 会直接编译失败,除非改成借用或显式克隆。
  2. No implicit nulls: data would have to be Option<String> and the None case must be handled before calling string methods.
    2. 没有隐式空值data 必须被写成 Option<String>,并且在调用字符串方法前先把 None 分支处理掉。
  3. Thread-safety markers: shared mutable state across threads must be wrapped in something like Arc<Mutex<Vec<i32>>>, and the compiler checks the access pattern.
    3. 线程安全约束:跨线程共享可变状态通常要包进 Arc<Mutex<Vec<i32>>> 之类结构,而且访问方式会被编译器严格检查。

Key takeaway: Rust shifts a lot of “this might explode later” uncertainty into “this does not compile until the ownership and safety story is coherent.”
核心收获:Rust 会把大量“以后可能炸”的不确定性,前移成“在所有权和安全逻辑说通之前根本编不过”。