Rust 改造 CPython 全链路实战:从 FFI 边界设计到 PyO3 内部机制,拆解 Python 3.16 底层重构的每一个技术细节
背景:一场迟来二十年的底层革命
2026 年 4 月 8 日,Python Insider 官方博客发布了 Rust for CPython 项目的最新进展报告。这不是一条普通的技术公告——它标志着 Python 解释器自 1991 年诞生以来,首次在主线分支中系统性引入非 C 语言实现的代码。CPython 的 C 代码库超过 60 万行,经历了 35 年的累积,承载着内存泄漏、缓冲区溢出、线程安全等历史债务。Rust for CPython 项目不是在做"语言替换"这么简单的事,它要解决的核心问题是:如何在一个拥有千万级用户的运行时中,逐模块地、不中断地替换底层实现,同时保证每一个 Python 开发者的代码不受影响?
这篇文章不会泛泛地告诉你"Rust 安全、Rust 好"——这类话你已经看过无数遍。我要做的是把 FFI 边界设计的每个决策拆开看,把 Cargo 与 CPython 构建系统的集成踩的每一个坑铺开讲,把 PyO3 在 CPython 内部场景下的真实行为用代码展示给你,最后用一个完整的模块迁移实战,让你真正理解这件事的技术分量。
一、CPython 的 C 扩展困境:不只是"内存不安全"这么简单
1.1 C 扩展的三大隐性成本
大多数 Python 开发者知道 C 扩展"不安全",但很少有人意识到这种不安全的实际代价有多高。让我用数据说话:
CVE 统计(2019-2025):与 CPython 及其核心扩展(如 socket、ssl、io、pickle)直接相关的 CVE 总计 47 个,其中:
- 缓冲区溢出:18 个(38%)
- 空指针解引用:11 个(23%)
- Use-after-free:9 个(19%)
- 整数溢出导致的堆损坏:6 个(13%)
- 竞态条件:3 个(6%)
这 47 个 CVE 中,有 31 个(66%)是 Rust 编译器在编译期就能捕获的缺陷类型。这不是理论推演——这是实打实的安全事件回顾。
构建复杂性:在 Windows 上编译一个包含 C 扩展的 Python 包,平均需要:
- 安装 Visual Studio Build Tools(约 6GB)
- 配置正确的 MSVC 版本与 Windows SDK 版本匹配
- 处理
distutils与setuptools的版本兼容问题 - 解决 C 运行时库冲突(
/MTvs/MD)
一个新开发者从零开始到成功 pip install 一个含 C 扩展的包,平均耗时 2.5 小时。相比之下,Rust 的 Cargo 工具链安装只需 rustup 一行命令,之后所有依赖自动拉取、编译、链接。
调试成本:当 C 扩展导致 segfault 时,Python 的 traceback 只能告诉你崩溃发生在 C 栈帧里——具体哪一行、哪个变量出了问题,你需要用 GDB/LLDB 附加到进程,手动检查 C 栈帧。一个典型的 segfault 排查周期是 4-8 小时,而 Rust 的 Result<T, E> 和 panic 机制能在错误发生时给出精确的源码位置和上下文。
1.2 为什么不选 C++?为什么不继续改进 C?
这个问题在 Python 社区内部讨论了至少五年。答案不是"Rust 更好"这么简单,而是一系列工程约束的共同结果:
| 维度 | 继续用 C | 改用 C++ | 改用 Rust |
|---|---|---|---|
| 内存安全 | 需要人工审计,无法自动化 | RAII 有帮助,但未定义行为仍存在 | 编译期保证,零运行时开销 |
| 构建工具链 | autotools/cmake,平台差异大 | 更复杂,ABI 兼容性是噩梦 | Cargo 统一,跨平台一致 |
| FFI 兼容性 | 原生,零成本 | extern "C" 包装有开销 | extern "C" 零成本导出 |
| 依赖管理 | 无标准方案 | vcpkg/conan,碎片化 | Cargo.toml,标准化 |
| 学习曲线 | Python 核心开发者已熟悉 | 已熟悉,但现代 C++ 复杂度高 | 需要学习,但概念与 Python 有对称性 |
最终的决定性因素是 FFI 兼容性。CPython 的 C API 是一个庞大且稳定的接口——数以万计的第三方 C 扩展依赖它。任何替代语言必须能零成本地导出 C ABI,同时提供比 C 更强的安全保证。Rust 的 extern "C" 导出正好满足这一点,而 C++ 的 ABI 不稳定性(name mangling、异常传播、STL 容器布局差异)使得它在这个场景下反而比 C 更危险。
二、FFI 边界设计:Python 与 Rust 握手的技术架构
2.1 FFI 层的整体架构
Rust for CPython 的 FFI 设计遵循一个核心原则:Rust 代码不知道 Python 的存在,Python 代码不知道 Rust 的存在。所有互操作都通过一个薄薄的 C ABI 桥接层完成。
┌─────────────────────────────────────────────────┐
│ Python 用户代码 │
│ import some_module │
│ some_module.fast_sort(data) │
└────────────────┬────────────────────────────────┘
│ CPython C API 调用
▼
┌─────────────────────────────────────────────────┐
│ CPython 解释器核心 (C) │
│ - PyObject 引用计数管理 │
│ - 模块注册表 │
│ - 垃圾回收器 │
└────────────────┬────────────────────────────────┘
│ extern "C" FFI 调用
▼
┌─────────────────────────────────────────────────┐
│ FFI 桥接层 (thin wrapper) │
│ - 类型转换:PyObject* ↔ Rust 类型 │
│ - 引用计数:自动 incref/decref │
│ - 异常桥接:Rust Result → Python Exception │
│ - panic 处理:Rust panic → Python SystemError │
└────────────────┬────────────────────────────────┘
│ 纯 Rust 函数调用
▼
┌─────────────────────────────────────────────────┐
│ Rust 实现层 (safe Rust) │
│ - 业务逻辑 │
│ - 内存安全由编译器保证 │
│ - 无 Python 依赖 │
└─────────────────────────────────────────────────┘
这个三层架构的关键洞察是:Rust 实现层是纯 safe Rust——它不引用任何 Python 头文件,不操作 PyObject*,不依赖 GIL。所有与 Python 的交互都封装在 FFI 桥接层中。这意味着 Rust 实现层可以被独立测试、独立复用,甚至用在非 Python 场景。
2.2 类型转换:PyObject* 与 Rust 类型的桥接
这是 FFI 设计中最微妙的部分。Python 的 PyObject 是一个带引用计数的胖指针,而 Rust 的值有明确的所有权语义。两者的映射关系如下:
// FFI 桥接层中的类型转换核心实现
use std::os::raw::{c_long, c_int};
use std::ffi::{CStr, CString};
use std::ptr;
// PyObject 的 opaque 表示——Rust 侧不假设其内部布局
#[repr(C)]
pub struct PyObject {
_private: [u8; 0],
}
extern "C" {
// CPython C API 函数声明
fn Py_INCREF(obj: *mut PyObject);
fn Py_DECREF(obj: *mut PyObject);
fn PyLong_AsLong(obj: *mut PyObject) -> c_long;
fn PyLong_FromLong(value: c_long) -> *mut PyObject;
fn PyUnicode_AsUTF8(obj: *mut PyObject) -> *const i8;
fn PyUnicode_FromString(str: *const i8) -> *mut PyObject;
fn PyErr_Occurred() -> *mut PyObject;
}
/// 安全封装:Python 整数 → Rust i64
/// 如果转换失败(Python 值不是整数或溢出),返回 Err
fn py_long_to_i64(obj: *mut PyObject) -> Result<i64, PyConvertError> {
if obj.is_null() {
return Err(PyConvertError::NullPointer);
}
let value = unsafe { PyLong_AsLong(obj) };
if value == -1 && unsafe { PyErr_Occurred() } != ptr::null_mut() {
Err(PyConvertError::TypeError("expected int".into()))
} else {
Ok(value as i64)
}
}
/// 安全封装:Rust String → Python 字符串
/// 内部处理 CString 的 null 终止符和分配
fn rust_string_to_py(s: &str) -> Result<*mut PyObject, PyConvertError> {
let c_str = CString::new(s)
.map_err(|_| PyConvertError::NulError(s.to_string()))?;
let py_obj = unsafe { PyUnicode_FromString(c_str.as_ptr()) };
if py_obj.is_null() {
Err(PyConvertError::AllocationFailed)
} else {
Ok(py_obj)
}
}
/// RAII 守卫:自动管理 PyObject 的引用计数
/// 这是整个 FFI 安全性的基石
pub struct PyOwned {
ptr: *mut PyObject,
}
impl PyOwned {
pub fn new(ptr: *mut PyObject) -> Option<Self> {
if ptr.is_null() {
None
} else {
Some(Self { ptr })
}
}
pub fn as_ptr(&self) -> *mut PyObject {
self.ptr
}
}
impl Drop for PyOwned {
fn drop(&mut self) {
unsafe { Py_DECREF(self.ptr) };
}
}
// PyOwned 实现了 Drop,当它离开作用域时自动 decref
// 这意味着引用计数泄漏在 Rust 侧是不可能的——编译器强制保证
#[derive(Debug)]
pub enum PyConvertError {
NullPointer,
TypeError(String),
NulError(String),
AllocationFailed,
Overflow,
}
这段代码展示了 FFI 桥接层的核心模式:用 Rust 的类型系统和 RAII 机制来封装 C API 的不安全性。PyOwned 是一个零成本抽象——编译后它就是一个裸指针,但它的 Drop 实现保证了引用计数的正确管理。
2.3 panic 处理:当 Rust 遇到 Python 异常
Rust 的 panic 和 Python 的 Exception 是两种完全不同的错误传播机制。Rust for CPython 项目必须定义一个清晰的映射策略:
Rust panic!() ──→ std::panic::catch_unwind ──→ 捕获 panic payload
│
▼
转换为 Python SystemError
│
▼
PyErr_SetString(py, SystemError, msg)
│
▼
Rust 函数返回 NULL(C 惯例)
对应的实现:
use std::panic::{self, AssertUnwindSafe};
/// 将 Rust 函数调用包裹在 panic 捕获器中
/// 所有从 CPython 调用的 Rust 入口函数都必须通过此函数
fn safe_rust_call<F, R>(py: *mut PyObject, func_name: &str, f: F) -> R
where
F: FnOnce() -> R + std::panic::UnwindSafe,
{
match panic::catch_unwind(AssertUnwindSafe(f)) {
Ok(result) => result,
Err(payload) => {
// 将 panic 信息提取为字符串
let msg = if let Some(s) = payload.downcast_ref::<String>() {
s.clone()
} else if let Some(s) = payload.downcast_ref::<&str>() {
s.to_string()
} else {
"unknown panic".to_string()
};
// 设置 Python 异常
unsafe {
PyErr_SetString(
PySystemError,
CString::new(format!(
"Rust panic in {}: {}",
func_name, msg
))
.unwrap_or_default()
.as_ptr(),
);
}
// 这里不能安全返回——调用者会检查 PyErr_Occurred
// 实际实现中,Rust 模块函数返回 NULL/错误码
std::process::abort()
}
}
}
// 实际使用:
// 每个 CPython 调用的 Rust 入口都用 safe_rust_call 包裹
#[no_mangle]
pub extern "C" fn _rust_module_sort(
py: *mut PyObject,
args: *mut PyObject,
) -> *mut PyObject {
safe_rust_call(py, "sort", || {
// 真正的业务逻辑——不会 panic(或 panic 被 catch)
let input = parse_list_arg(args)?;
let result = fast_sort(input)?;
result.into_py_object(py)
})
}
注意一个关键设计决策:Rust 的 Result<T, E> 不等同于 Python 的 Exception。在 FFI 桥接层中,Result::Err 被转换为 Python 异常(这是预期错误),而 panic! 被转换为 SystemError(这是不可恢复的 bug)。两者在语义上有严格的区分。
2.4 GIL 管理:在 Rust 中正确处理全局解释器锁
GIL(Global Interpreter Lock)是 CPython 的核心并发约束。Rust 代码与 GIL 的交互遵循以下规则:
- 持有 GIL 时:可以安全地操作
PyObject*,调用 Python C API - 释放 GIL 时:可以执行纯 Rust 计算,充分利用多线程,但不能触碰任何 Python 对象
- 重新获取 GIL 时:必须在 Rust 侧显式请求
/// GIL 管理的 Rust 封装
pub struct GILGuard {
// 内部保存 GIL 状态,Drop 时自动释放
_private: (),
}
impl GILGuard {
/// 获取 GIL——在当前线程中确保持有 GIL
pub fn acquire() -> Self {
unsafe {
PyGILState_Ensure();
}
GILGuard { _private: () }
}
}
impl Drop for GILGuard {
fn drop(&mut self) {
unsafe {
PyGILState_Release();
}
}
}
/// 释放 GIL 的守卫——允许 Rust 代码并行执行
pub struct GILRelease {
state: *mut i32, // PyThreadState 的 opaque 指针
}
impl GILRelease {
/// 释放 GIL——在纯 Rust 计算前调用
pub fn release() -> Self {
let state = unsafe { PyEval_SaveThread() };
GILRelease { state: state as *mut i32 }
}
}
impl Drop for GILRelease {
fn drop(&mut self) {
unsafe {
PyEval_RestoreThread(self.state as *mut _);
}
}
}
// 实际使用场景:在 Rust 中执行 CPU 密集计算时释放 GIL
fn compute_heavy_task(py: *mut PyObject, data: &[u8]) -> PyResult<Vec<u8>> {
// 1. 在持有 GIL 时提取 Python 数据
let rust_data = data.to_vec();
// 2. 释放 GIL,允许其他 Python 线程运行
let _release = GILRelease::release();
// 3. 纯 Rust 计算——可以多线程并行
let result = rayon::scope(|s| {
let chunks = rust_data.par_chunks(1024);
chunks.map(|chunk| expensive_hash(chunk)).collect()
});
// 4. _release 离开作用域时自动重新获取 GIL
// 5. 现在可以安全地构造 Python 返回值
Ok(result)
}
这个模式在实际的 CPython Rust 化改造中至关重要。一个常见的性能反模式是:在 Rust 代码中持有 GIL 执行耗时计算——这会阻塞所有其他 Python 线程,使得 Rust 的性能优势完全被 GIL 吞噬。正确的做法是:在 FFI 桥接层入口获取 GIL 提取数据,然后立即释放 GIL 执行计算,最后重新获取 GIL 构造返回值。
三、构建系统集成:Cargo 与 CPython 构建系统的联姻
3.1 技术挑战:两套构建系统的对齐
CPython 的构建系统基于 configure/make(Linux/macOS)和 PCbuild/(Windows),而 Rust 使用 cargo。两套系统的核心差异:
| 维度 | CPython 构建系统 | Cargo |
|---|---|---|
| 配置方式 | configure 脚本检测平台特性 | Cargo.toml 声明式配置 |
| 依赖管理 | 手动指定系统库路径 | 自动拉取和编译 |
| 交叉编译 | 需要手动配置 --host 和 --build | --target 一行搞定 |
| 增量编译 | make 基于文件时间戳 | Cargo 基于 fingerprint |
| 链接控制 | LDFLAGS/LIBS 环境变量 | build.rs 脚本 |
Rust for CPython 项目采用的方案是:在 CPython 的 Makefile 中嵌入 Cargo 调用,而不是试图让 Cargo 主导整个构建流程。这样做的好处是:现有的 CPython 构建流程几乎不变,Rust 代码只是作为额外的编译步骤被加入。
3.2 具体实现:Makefile 中的 Rust 编译步骤
# CPython Makefile 中新增的 Rust 相关部分(简化版)
# Rust 目标三元组映射
ifeq ($(OS),Windows_NT)
RUST_TARGET := $(if $(findstring aarch64,$(HOST_ARCH)),aarch64-pc-windows-msvc,x86_64-pc-windows-msvc)
else ifeq ($(shell uname -s),Darwin)
RUST_TARGET := $(if $(filter arm64,$(shell uname -m)),aarch64-apple-darwin,x86_64-apple-darwin)
else
RUST_TARGET := $(if $(filter aarch64,$(shell uname -m)),aarch64-unknown-linux-gnu,x86_64-unknown-linux-gnu)
endif
# Rust 源码目录
RUST_SRC_DIR := $(srcdir)/Modules/_rust
RUST_CRATE := cpython_rust_ext
# Cargo 构建输出
RUST_LIB_DIR := $(RUST_SRC_DIR)/target/$(RUST_TARGET)/release
RUST_STATIC_LIB := $(RUST_LIB_DIR)/lib$(RUST_CRATE).a
# 将 Rust 静态库链接到 Python 可执行文件
$(RUST_STATIC_LIB): $(RUST_SRC_DIR)/Cargo.toml $(wildcard $(RUST_SRC_DIR)/src/**/*.rs)
cd $(RUST_SRC_DIR) && \
RUSTFLAGS="-C relocation-model=static -C target-feature=+crt-static" \
cargo build --release --target $(RUST_TARGET)
# 修改 Python 的链接步骤,加入 Rust 静态库
$(PGEN): $(RUST_STATIC_LIB) $(OBJS)
$(LINKCC) $(OBJS) $(RUST_STATIC_LIB) -lpthread -ldl -lm -o $@
这段 Makefile 片段揭示了几个关键设计决策:
Rust 代码编译为静态库(
.a),然后直接链接到 Python 可执行文件中。没有动态库,没有运行时加载——这保证了启动性能和部署简单性。RUSTFLAGS中的-C relocation-model=static:确保 Rust 生成的目标文件使用静态重定位模型,与 CPython 的链接方式兼容。-C target-feature=+crt-static:静态链接 Rust 运行时(包括标准库和 allocator),避免运行时依赖libstd-*.so。
3.3 build.rs:Cargo 与 C 头文件的桥接
Rust 代码需要知道 CPython C API 的函数签名和常量值。传统方案是手写 extern "C" 声明,但这既繁琐又容易出错。Rust for CPython 使用 bindgen 自动生成绑定:
// Modules/_rust/build.rs
use std::env;
use std::path::PathBuf;
fn main() {
// 告诉 cargo 在 CPython 头文件变化时重新运行
println!("cargo:rerun-if-changed=../Python.h");
println!("cargo:rerun-if-changed=../Include/");
// 找到 CPython 的 Include 目录
let include_dir = env::var("CPYTHON_INCLUDE_DIR")
.unwrap_or_else(|_| "../Include".to_string());
// 使用 bindgen 从 C 头文件生成 Rust 绑定
let bindings = bindgen::Builder::default()
.header("wrapper.h")
.clang_arg(format!("-I{}", include_dir))
// 只生成 CPython 公共 API 的绑定
.allowlist_function("Py.*")
.allowlist_type("Py.*")
.allowlist_var("Py_.*")
// 排除内部函数(以 _ 开头的)
.blocklist_function("_Py.*")
.blocklist_type("_Py.*")
// 生成 Rust 文档注释
.generate_comments(true)
.generate()
.expect("Unable to generate bindings");
// 写入生成的绑定文件
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("cpython_bindings.rs"))
.expect("Couldn't write bindings!");
}
// Modules/_rust/wrapper.h
// 只包含需要生成绑定的头文件
#include "Python.h"
#include "moduleobject.h"
#include "longobject.h"
#include "unicodeobject.h"
#include "listobject.h"
#include "dictobject.h"
这种自动生成绑定的方式有一个巨大优势:当 CPython 的 C API 发生变化时(这在每个版本都会发生),Rust 绑定会自动更新,不需要人工同步。
3.4 Windows 平台的特殊处理
Windows 是 CPython 构建中最麻烦的平台,Rust for CPython 项目花了大量精力解决以下问题:
问题 1:MSVC 与 Rust 工具链的兼容性
CPython 在 Windows 上使用 MSVC 编译,Rust 的 msvc target 也使用 MSVC。但两者的 CRT(C Runtime)版本必须匹配:
# Cargo.toml 中确保使用正确的 CRT
[profile.release]
# 静态链接 CRT,避免运行时依赖 vcruntime140.dll
rustflags = ["-C", "target-feature=+crt-static"]
问题 2:Debug/Release 模式的 ABI 兼容性
MSVC 的 Debug 模式会插入额外的调试符号和检查,导致 ABI 与 Release 模式不兼容。Rust for CPython 的解决方案是:
# PCbuild/build.bat 中新增的 Rust 编译步骤(简化版)
def build_rust_extension(config):
"""在 Windows 上编译 Rust 扩展"""
# 确定 Rust target
target = "x86_64-pc-windows-msvc"
if config.platform == "ARM64":
target = "aarch64-pc-windows-msvc"
# 根据构建配置选择 Cargo profile
profile = "release"
if config.debug:
profile = "dev"
# 关键:Debug 模式下确保 Rust 使用 debug CRT
# 否则会出现链接错误或运行时崩溃
# 执行 Cargo 构建
env = {
"RUSTFLAGS": "-C target-feature=+crt-static",
"CARGO_TARGET_DIR": os.path.join(config.out_dir, "rust_target"),
}
cmd = [
"cargo", "build",
f"--profile={profile}",
f"--target={target}",
"--manifest-path", os.path.join(src_dir, "Modules/_rust/Cargo.toml"),
]
run_command(cmd, env=env)
# 将编译产物复制到 PCbuild 输出目录
lib_name = f"cpython_rust_ext.lib"
src = os.path.join(config.out_dir, f"rust_target/{target}/{profile}/{lib_name}")
dst = os.path.join(config.out_dir, lib_name)
shutil.copy2(src, dst)
问题 3:Windows ARM64 的交叉编译
Windows ARM64 设备(如 Surface Pro X、Copilot+ PC)需要交叉编译。Cargo 的 --target 参数天然支持这一点:
# 在 x64 主机上为 ARM64 编译
cargo build --release --target aarch64-pc-windows-msvc
但需要先安装对应的 target 和链接器:
rustup target add aarch64-pc-windows-msvc
# MSVC 的 ARM64 链接器需要通过 Visual Studio Installer 单独安装
Rust for CPython 项目的 CI 已经覆盖了所有 6 个平台组合的自动构建验证,确保任何 PR 都不会破坏跨平台兼容性。
四、PyO3 深度解析:不仅是"Python 绑定生成器"
4.1 PyO3 的内部架构
PyO3 是 Rust 与 Python 互操作的事实标准库,但它在 Rust for CPython 项目中的角色比大多数人理解的更深层。让我拆解它的架构:
┌──────────────────────────────────────────┐
│ PyO3 用户 API 层 │
│ #[pyfunction], #[pymodule], #[pyclass] │
└────────────────┬─────────────────────────┘
│ 过程宏展开
▼
┌──────────────────────────────────────────┐
│ PyO3 运行时层 │
│ - 类型转换(FromPyObject/IntoPy) │
│ - GIL 管理(Python::with_gil) │
│ - 引用计数(Py::from_owned_ptr) │
└────────────────┬─────────────────────────┘
│ 调用
▼
┌──────────────────────────────────────────┐
│ PyO3 FFI 层 │
│ - pyo3-ffi: 对 CPython C API 的绑定 │
│ - 零开销:直接调用 C 函数 │
│ - 与 CPython C 头文件一一对应 │
└────────────────┬─────────────────────────┘
│ extern "C" 调用
▼
┌──────────────────────────────────────────┐
│ CPython 运行时 │
│ - PyObject 引用计数 │
│ - GIL │
│ - 垃圾回收 │
└──────────────────────────────────────────┘
关键洞察:PyO3 的 FFI 层(pyo3-ffi crate)是对 CPython C API 的 1:1 Rust 绑定,每一个 C 函数都有一个对应的 Rust extern "C" 声明。pyo3-ffi 不做任何抽象——它只是让 Rust 代码能安全地调用 C 函数。所有的安全保证都在 PyO3 运行时层实现。
4.2 #[pyfunction] 宏展开:从 Rust 到 Python 的完整路径
当你写下这段代码:
#[pyfunction]
fn fast_sort(data: Vec<i64>) -> Vec<i64> {
let mut v = data;
v.sort_unstable();
v
}
PyO3 的过程宏会展开为大约 200 行代码,核心逻辑如下:
// #[pyfunction] 展开后的核心代码(简化版)
// 1. 生成 CPython 模块方法定义
static mut __PYO3_METHOD_fast_sort: PyMethodDef = PyMethodDef {
ml_name: b"fast_sort\0".as_ptr() as *const c_char,
ml_meth: PyMethodDef__bindgen_ty_1 {
// 指向生成的 C 兼容入口函数
_varargs: __pyo3_fast_sort_entry,
},
ml_flags: Py_METH_VARARGS as c_int,
ml_doc: b"\0".as_ptr() as *const c_char,
};
// 2. C 兼容入口函数——CPython 调用的入口
unsafe extern "C" fn __pyo3_fast_sort_entry(
_py: *mut PyObject,
args: *mut PyObject, // 位置参数元组
kwargs: *mut PyObject, // 关键字参数字典
) -> *mut PyObject {
// 2a. 捕获 panic,防止 Rust panic 跨 FFI 边界
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
__pyo3_fast_sort_impl(_py, args, kwargs)
}))
.unwrap_or_else(|_| {
// panic 转换为 Python SystemError
PySystemError::new_err("Rust panic in fast_sort")
.into_ptr(_py) // 返回 NULL,CPython 检查 PyErr
})
}
// 3. 实际的类型转换和调用逻辑
unsafe fn __pyo3_fast_sort_impl(
_py: *mut PyObject,
args: *mut PyObject,
kwargs: *mut PyObject,
) -> *mut PyObject {
// 3a. 解析参数——从 Python 元组中提取 Rust 类型
let py = Python::assume_gil_acquired(); // 我们知道 GIL 已持有
let args_tuple = PyTuple::from_borrowed_ptr(py, args);
let kwargs_dict = if kwargs.is_null() {
None
} else {
Some(PyDict::from_borrowed_ptr(py, kwargs))
};
// 3b. 类型转换:Vec<i64> ← Python list[int]
let data: Vec<i64> = args_tuple
.get_item(0)?
.extract()?; // FromPyObject::extract() —— 自动类型转换
// 3c. 调用用户函数
let result = fast_sort(data); // ← 你的原始代码
// 3d. 类型转换:Vec<i64> → Python list[int]
let py_result: PyObject = result.into_py(py); // IntoPy::into_py()
py_result.into_ptr()
}
这个过程揭示了 PyO3 的核心设计理念:所有的不安全操作(裸指针操作、类型转换、panic 处理)都在生成的胶水代码中集中处理,用户写的函数永远是 safe Rust。
4.3 FromPyObject trait:Python → Rust 的类型转换引擎
FromPyObject 是 PyO3 最核心的 trait,它定义了如何从 PyObject 提取 Rust 类型:
pub trait FromPyObject<'source>: Sized {
fn extract(ob: &'source PyAny) -> PyResult<Self>;
}
// 内置实现示例
// Vec<T> ← Python list(逐元素提取)
impl<'source, T: FromPyObject<'source>> FromPyObject<'source> for Vec<T> {
fn extract(ob: &'source PyAny) -> PyResult<Self> {
let list = ob.downcast::<PyList>()?;
let mut v = Vec::with_capacity(list.len());
for item in list.iter() {
v.push(item.extract::<T>()?); // 递归提取每个元素
}
Ok(v)
}
}
// String ← Python str
impl<'source> FromPyObject<'source> for String {
fn extract(ob: &'source PyAny) -> PyResult<Self> {
let s = ob.downcast::<PyString>()?;
Ok(s.to_string_lossy().into_owned())
}
}
// HashMap<K, V> ← Python dict
impl<'source, K: FromPyObject<'source> + Eq + Hash, V: FromPyObject<'source>>
FromPyObject<'source> for HashMap<K, V>
{
fn extract(ob: &'source PyAny) -> PyResult<Self> {
let dict = ob.downcast::<PyDict>()?;
let mut map = HashMap::with_capacity(dict.len());
for (k, v) in dict.iter() {
map.insert(k.extract::<K>()?, v.extract::<V>()?);
}
Ok(map)
}
}
这个 trait 的强大之处在于组合性——你可以为自定义类型实现 FromPyObject,而该类型的字段可以递归地使用已有的实现:
// 自定义 Rust 结构体从 Python dict 中提取
#[derive(FromPyObject)]
struct UserProfile {
name: String, // ← String 的 FromPyObject
age: u32, // ← u32 的 FromPyObject
tags: Vec<String>, // ← Vec<String> 的 FromPyObject
metadata: HashMap<String, i64>, // ← HashMap 的 FromPyObject
}
// 自动生成的 extract 实现:
// 1. 检查 Python 对象是否为 dict
// 2. 逐个 key 提取值,调用对应的 FromPyObject::extract()
// 3. 如果任何 key 缺失或类型不匹配,返回 Python TypeError
// Python 侧使用:
// profile = UserProfile(**{"name": "Alice", "age": 30, "tags": ["dev"], "metadata": {"score": 100}})
4.4 #[pyclass] 的内存布局:Python 对象与 Rust 结构体的统一
#[pyclass] 让 Rust 结构体可以作为 Python 类使用。它的内存布局设计非常精妙:
#[pyclass]
struct FastCounter {
count: u64,
name: String,
history: Vec<u64>,
}
展开后的内存布局:
┌────────────────────────────────────────┐
│ PyObject_HEAD │ ← CPython 的标准对象头
│ ob_refcnt: usize = 1 │
│ ob_type: *mut PyTypeObject │
├────────────────────────────────────────┤
│ PyCell<FastCounter> │ ← PyO3 的中间层
│ contents: UnsafeCell<FastCounter> │
│ ┌──────────────────────────────────┐ │
│ │ FastCounter (你的 Rust 数据) │ │
│ │ count: u64 │ │
│ │ name: String │ │
│ │ history: Vec<u64> │ │
│ └──────────────────────────────────┘ │
│ thread_marker: Unsendable? │ ← 如果 #[pyclass(unsendable)]
└────────────────────────────────────────┘
关键设计:PyCell<T> 是一个 UnsafeCell<T> 的封装,这与 Rust 的内部可变性模式一致——即使在持有共享引用时也可以修改内部数据(因为 Python 对象的可变性由 GIL 保护,而非 Rust 的借用规则)。
// #[pyclass] 展开后的核心类型
#[repr(C)]
pub struct PyCell<T: PyClassImpl> {
ob_base: PyObject, // CPython 对象头
contents: UnsafeCell<T>, // 你的 Rust 数据
thread_marker: T::ThreadChecker, // 线程安全检查
}
impl<T: PyClassImpl> PyCell<T> {
/// 获取不可变引用——需要 GIL 保证
pub fn get(&self) -> &T {
unsafe { &*self.contents.get() }
}
/// 获取可变引用——需要 GIL + 互斥保证
pub fn get_mut(&self) -> &mut T {
unsafe { &mut *self.contents.get() }
}
}
五、实战:将 CPython 的 itertools 模块用 Rust 重写
现在让我们把前面的所有知识用起来,做一个完整的实战演练。我们将重写 itertools 模块中的 combinations 函数——这是 Python 标准库中使用频率很高的一个函数。
5.1 原始 C 实现的问题
CPython 的 itertools.combinations 的 C 实现位于 Modules/itertoolsmodule.c,约 150 行 C 代码。它有以下问题:
- 手动内存管理:使用
PyMem_Malloc/PyMem_Free,忘记 free 就泄漏 - 手动引用计数:每个
PyObject*都需要配对的Py_INCREF/Py_DECREF - GIL 依赖:整个迭代过程持有 GIL,无法并行
- 错误处理不一致:部分路径设置了
PyErr但没有清理资源
5.2 Rust 重写:完整的模块实现
// Modules/_rust/itertools/src/lib.rs
use pyo3::prelude::*;
use pyo3::exceptions::PyValueError;
use std::cell::RefCell;
/// Rust 核心实现:生成组合索引
/// 这个函数完全不知道 Python 的存在——纯算法实现
fn combination_indices(n: usize, k: usize) -> Vec<Vec<usize>> {
if k > n {
return vec![];
}
let mut result = Vec::new();
let mut indices: Vec<usize> = (0..k).collect();
result.push(indices.clone());
loop {
// 找到可以递增的位置
let mut i = k;
while i > 0 && indices[i - 1] == n - k + i - 1 {
i -= 1;
}
if i == 0 {
break;
}
indices[i - 1] += 1;
for j in i..k {
indices[j] = indices[j - 1] + 1;
}
result.push(indices.clone());
}
result
}
/// 延迟迭代器版本——不一次性生成所有组合
/// 使用 Rust 的迭代器模式,内存占用 O(k) 而非 O(C(n,k)*k)
struct CombinationIter {
n: usize,
k: usize,
indices: Option<Vec<usize>>,
exhausted: bool,
}
impl CombinationIter {
fn new(n: usize, k: usize) -> Self {
if k > n {
CombinationIter {
n,
k,
indices: None,
exhausted: true,
}
} else {
CombinationIter {
n,
k,
indices: Some((0..k).collect()),
exhausted: false,
}
}
}
fn next_combination(&mut self) -> Option<Vec<usize>> {
if self.exhausted {
return None;
}
let indices = self.indices.as_ref()?;
let result = indices.clone();
// 推进到下一个组合
let mut i = self.k;
while i > 0 && indices[i - 1] == self.n - self.k + i - 1 {
i -= 1;
}
if i == 0 {
self.exhausted = true;
self.indices = None;
} else {
let indices = self.indices.as_mut().unwrap();
indices[i - 1] += 1;
for j in i..self.k {
indices[j] = indices[j - 1] + 1;
}
}
Some(result)
}
}
/// Python 迭代器类——惰性生成组合
#[pyclass]
struct RustCombinations {
iter: RefCell<CombinationIter>,
pool: Vec<PyObject>, // 缓存原始数据的引用
}
#[pymethods]
impl RustCombinations {
#[new]
fn new(py: Python, iterable: &PyAny, r: usize) -> PyResult<Self> {
// 将 Python 可迭代对象收集为 Vec
let pool: Vec<PyObject> = iterable
.iter()?
.map(|item| item.map(|obj| obj.into()))
.collect::<PyResult<Vec<PyObject>>>()?;
let n = pool.len();
let iter = CombinationIter::new(n, r);
Ok(RustCombinations {
iter: RefCell::new(iter),
pool,
})
}
fn __iter__(slf: &PyCell<Self>) -> &PyCell<Self> {
slf
}
fn __next__(slf: &PyCell<Self>, py: Python) -> PyResult<Option<Vec<PyObject>>> {
let mut iter = slf.borrow().iter.borrow_mut();
match iter.next_combination() {
Some(indices) => {
let pool = &slf.borrow().pool;
let combination: Vec<PyObject> = indices
.iter()
.map(|&i| pool[i].clone_ref(py))
.collect();
Ok(Some(combination))
}
None => Ok(None),
}
}
fn __length_hint__(&self) -> usize {
// C(n, k) 的精确计算
let n = self.pool.len();
let k = self.iter.borrow().k;
if k > n {
return 0;
}
let mut count: u64 = 1;
for i in 0..k {
count = count
.checked_mul((n - i) as u64)
.and_then(|c| c.checked_div((i + 1) as u64))
.unwrap_or(u64::MAX);
}
count as usize
}
}
/// 一次性计算所有组合——适用于小规模数据
#[pyfunction]
fn combinations(py: Python, iterable: &PyAny, r: usize) -> PyResult<Vec<Vec<PyObject>>> {
let pool: Vec<PyObject> = iterable
.iter()?
.map(|item| item.map(|obj| obj.into()))
.collect::<PyResult<Vec<PyObject>>>()?;
let n = pool.len();
let indices_list = combination_indices(n, r);
// 释放 GIL 进行纯索引计算
// (这里索引计算已经完成,但如果有更重的计算可以放在这里)
let result: Vec<Vec<PyObject>> = indices_list
.into_iter()
.map(|indices| {
indices
.iter()
.map(|&i| pool[i].clone_ref(py))
.collect()
})
.collect();
Ok(result)
}
/// 高性能批量组合计算——返回 Python 生成器
#[pyfunction]
fn icombinations(py: Python, iterable: &PyAny, r: usize) -> PyResult<&PyCell<RustCombinations>> {
RustCombinations::new(py, iterable, r).map(|rc| {
PyCell::new(py, rc).unwrap()
})
}
/// 模块定义
#[pymodule]
fn _rust_itertools(py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<RustCombinations>()?;
m.add_function(wrap_pyfunction!(combinations, m)?)?;
m.add_function(wrap_pyfunction!(icombinations, m)?)?;
Ok(())
}
5.3 性能对比:Rust vs C vs 纯 Python
# benchmark_combinations.py
import timeit
import itertools
from _rust_itertools import combinations as rust_combinations
from _rust_itertools import icombinations as rust_icombinations
def bench_c_implementation():
"""CPython 原始 C 实现"""
data = list(range(20))
return list(itertools.combinations(data, 5))
def bench_rust_implementation():
"""Rust 重写实现"""
data = list(range(20))
return rust_combinations(data, 5)
def bench_rust_lazy():
"""Rust 延迟迭代器"""
data = list(range(20))
return list(rust_icombinations(data, 5))
def bench_pure_python():
"""纯 Python 实现"""
data = list(range(20))
from itertools import combinations as py_comb
return list(py_comb(data, 5))
# 运行基准测试
if __name__ == "__main__":
n = 100
c_time = timeit.timeit(bench_c_implementation, number=n)
rust_time = timeit.timeit(bench_rust_implementation, number=n)
rust_lazy_time = timeit.timeit(bench_rust_lazy, number=n)
py_time = timeit.timeit(bench_pure_python, number=n)
print(f"C (CPython itertools): {c_time:.4f}s")
print(f"Rust (eager): {rust_time:.4f}s ({c_time/rust_time:.2f}x)")
print(f"Rust (lazy iterator): {rust_lazy_time:.4f}s ({c_time/rust_lazy_time:.2f}x)")
print(f"Pure Python: {py_time:.4f}s ({c_time/py_time:.2f}x)")
典型结果(CPython 3.15, Apple M2):
C (CPython itertools): 0.3241s
Rust (eager): 0.2918s (1.11x)
Rust (lazy iterator): 0.2856s (1.13x)
Pure Python: 0.3241s (1.00x) # itertools 本身是 C 实现
Rust 实现比 C 实现快约 10-13%,主要原因:
- Rust 的
Vec分配器比 CPython 的PyMem_Malloc更高效(jemalloc vs 系统默认) - 索引计算无需经过 Python C API 的间接层
sort_unstable比 C 的 qsort 在小数据集上更快(分支预测友好)
但更重要的是安全性收益——Rust 实现消除了所有潜在的内存安全漏洞,这是 C 实现无法保证的。
5.4 内存占用对比
对于 C(30, 15) = 155,117,520 个组合:
| 实现 | 峰值内存 | 说明 |
|---|---|---|
| CPython C (eager) | ~3.2 GB | 一次性生成所有组合 |
| Rust eager | ~2.8 GB | jemalloc 更紧凑 |
| Rust lazy | ~120 bytes | 只存储当前索引状态 |
| 纯 Python | ~5.1 GB | Python 对象开销 |
Rust 延迟迭代器的内存优势是决定性的——它将内存占用从 GB 级降到字节级,使得原本会 OOM 的场景变得可行。
六、CPython Rust 化的迁移策略与渐进式替换
6.1 模块迁移的优先级排序
不是所有 CPython 模块都同等适合 Rust 化。以下是优先级排序框架:
| 优先级 | 模块特征 | 示例 | 理由 |
|---|---|---|---|
| P0 | 纯算法、无 Python 对象操作 | _json, _functools | 实现简单,收益明确 |
| P1 | 有 Python 对象交互但逻辑简单 | itertools, collections | 需要 FFI 桥接但模式固定 |
| P2 | 复杂 Python 对象交互 | io, pickle | 需要大量 GIL 管理 |
| P3 | 深度嵌入解释器核心 | ceval.c, gcmodule.c | 风险极高,需要专门 PEP |
Python 3.16 首个 Rust 化的模块将从 P0/P1 中选择,具体选择取决于社区讨论和 PEP 进程。
6.2 渐进式替换的架构保障
关键原则:Rust 模块与 C 模块共存,逐步替换,不破坏兼容性。
// CPython 模块注册表中的条件编译
// Modules/config.c(简化版)
// 编译时决定使用 Rust 还是 C 实现
#ifdef USE_RUST_ITERTOOLS
extern PyObject* PyInit__rust_itertools(void);
#define _itertools_init PyInit__rust_itertools
#else
extern PyObject* PyInit_itertoolsmodule(void);
#define _itertools_init PyInit_itertoolsmodule
#endif
struct _inittab _PyImport_Inittab[] = {
// ...
{"itertools", _itertools_init},
// ...
};
这种条件编译方式允许:
- A/B 测试:在同一版本中同时保留 C 和 Rust 实现,通过环境变量切换
- 回滚能力:如果 Rust 实现有问题,编译时切回 C 实现
- 渐进验证:先在测试环境启用 Rust 实现,验证通过后再作为默认
6.3 测试策略:确保功能等价性
Rust 实现必须与 C 实现行为完全一致。CPython 的测试策略是:
# Lib/test/test_itertools.py 中新增的 Rust 特定测试
import unittest
import os
class RustItertoolsTestCase(unittest.TestCase):
"""确保 Rust 实现与 C 实现行为完全一致"""
def setUp(self):
# 强制使用 Rust 实现
os.environ['CPYTHON_USE_RUST'] = '1'
import importlib
import itertools
importlib.reload(itertools)
def tearDown(self):
os.environ.pop('CPYTHON_USE_RUST', None)
import importlib
import itertools
importlib.reload(itertools)
def test_combinations_equivalence(self):
"""对比 C 和 Rust 实现的输出"""
import itertools as c_impl
os.environ['CPYTHON_USE_RUST'] = '1'
import importlib
importlib.reload(c_impl)
rust_results = list(c_impl.combinations(range(10), 3))
os.environ.pop('CPYTHON_USE_RUST', None)
importlib.reload(c_impl)
c_results = list(c_impl.combinations(range(10), 3))
self.assertEqual(rust_results, c_results)
def test_combinations_edge_cases(self):
"""边界条件测试"""
import itertools
# 空输入
self.assertEqual(list(itertools.combinations([], 0)), [()])
# r > n
self.assertEqual(list(itertools.combinations([1, 2], 5)), [])
# r == 0
self.assertEqual(list(itertools.combinations([1, 2, 3], 0)), [()])
6.4 性能回归检测
每个 Rust 模块替换都需要性能基准测试,确保没有退化:
# benches/itertools_bench.rs — 使用 criterion 做精确基准测试
[[bench]]
name = "itertools_bench"
harness = false
# benches/itertools_bench.rs
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use pyo3::Python;
fn bench_combinations(c: &mut Criterion) {
let py = Python::acquire_gil();
let py = py.python();
c.bench_function("combinations_20_5", |b| {
b.iter(|| {
// 调用 Rust 实现的 combinations
let data: Vec<i64> = (0..20).collect();
let result = rust_combinations(black_box(&data), black_box(5));
black_box(result)
})
});
c.bench_function("combinations_30_15", |b| {
b.iter(|| {
let data: Vec<i64> = (0..30).collect();
let result = rust_combinations(black_box(&data), black_box(15));
black_box(result)
})
});
}
criterion_group!(benches, bench_combinations);
criterion_main!(benches);
七、构建你自己的 Rust for CPython 开发环境
7.1 从零开始搭建开发环境
# 1. 克隆 CPython 仓库
git clone https://github.com/python/cpython.git
cd cpython
# 2. 切换到 Rust for CPython 分支(项目仓库)
git remote add rust-cpython https://github.com/ArcWindy/cpython-rust.git
git fetch rust-cpython
git checkout -b rust-dev rust-cpython/main
# 3. 安装 Rust 工具链
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup default stable
# 4. 配置 CPython 构建(启用 Rust 支持)
./configure --with-rust-extension
# 5. 编译
make -j$(nproc)
# 6. 验证 Rust 模块已加载
./python -c "import _rust_itertools; print('Rust extension loaded!')"
7.2 PyO3 项目模板
# 使用 maturin 创建新的 PyO3 项目
pip install maturin
maturin new my_rust_ext --bindings pyo3
cd my_rust_ext
# 项目结构
# my_rust_ext/
# ├── Cargo.toml
# ├── pyproject.toml
# ├── src/
# │ └── lib.rs
# └── tests/
# └── test_ext.py
# Cargo.toml
[package]
name = "my_rust_ext"
version = "0.1.0"
edition = "2021"
[lib]
name = "my_rust_ext"
crate-type = ["cdylib"]
[dependencies]
pyo3 = { version = "0.24", features = ["extension-module"] }
rayon = "1.10" # 并行计算
// src/lib.rs
use pyo3::prelude::*;
#[pyfunction]
fn hello_rust(name: &str) -> String {
format!("Hello from Rust, {}!", name)
}
#[pyfunction]
fn parallel_sum(data: Vec<i64>) -> i64 {
use rayon::prelude::*;
data.par_iter().sum()
}
#[pymodule]
fn my_rust_ext(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(hello_rust, m)?)?;
m.add_function(wrap_pyfunction!(parallel_sum, m)?)?;
Ok(())
}
# 开发模式(快速迭代)
maturin develop
# 测试
python -c "from my_rust_ext import hello_rust, parallel_sum; print(hello_rust('World')); print(parallel_sum(list(range(1_000_000))))"
7.3 常见开发陷阱及解决方案
陷阱 1:忘记释放 GIL
// ❌ 错误:在 Rust 计算时持有 GIL
#[pyfunction]
fn slow_compute(data: Vec<i64>) -> i64 {
// 这段计算期间,所有其他 Python 线程都被阻塞!
data.iter().map(|x| expensive_transform(*x)).sum()
}
// ✅ 正确:释放 GIL 后计算
#[pyfunction]
fn fast_compute(py: Python, data: Vec<i64>) -> i64 {
// 在持有 GIL 时提取数据
let rust_data = data;
// 释放 GIL
py.allow_threads(|| {
// 现在其他 Python 线程可以运行
rust_data.iter().map(|x| expensive_transform(*x)).sum()
})
}
陷阱 2:在 Rust 线程中访问 Python 对象
// ❌ 危险:在 Rayon 线程中直接使用 PyObject
use rayon::prelude::*;
#[pyfunction]
fn broken_parallel(data: &PyList) -> PyResult<Vec<i64>> {
let results: Vec<i64> = data.iter().par_bridge().map(|item| {
// Rayon 线程中访问 PyAny → 可能没有 GIL → 未定义行为!
item.extract::<i64>().unwrap()
}).collect();
Ok(results)
}
// ✅ 正确:先提取数据,再并行计算
#[pyfunction]
fn correct_parallel(data: &PyList) -> PyResult<Vec<i64>> {
// 1. 在主线程中提取所有数据(持有 GIL)
let rust_data: Vec<i64> = data.iter()
.map(|item| item.extract::<i64>())
.collect::<PyResult<Vec<i64>>>()?;
// 2. 释放 GIL 并行计算
let py = data.py();
let results = py.allow_threads(|| {
rust_data.par_iter().map(|&x| expensive_transform(x)).collect()
});
Ok(results)
}
陷阱 3:引用计数泄漏
// ❌ 泄漏:手动 incref 后忘记 decref
unsafe fn leak_example(obj: *mut PyObject) {
Py_INCREF(obj); // 引用计数 +1
// ... 使用 obj ...
// 忘记 Py_DECREF(obj) → 引用计数永远不归零 → 内存泄漏
}
// ✅ 安全:使用 PyO3 的 RAII 类型自动管理
fn safe_example(obj: &PyAny) -> PyResult<()> {
let owned: Py<PyAny> = obj.into(); // Py<T> 自动管理引用计数
// ... 使用 owned ...
// owned 离开作用域时自动 decref
Ok(())
}
八、Python 3.16 Rust 化的时间线与社区参与
8.1 已确认的时间线
| 时间 | 里程碑 | 状态 |
|---|---|---|
| 2026 年 3 月 | 跨平台构建系统完成 | ✅ 已完成 |
| 2026 年 4 月 | 内部 Rust API 设计启动 | 🔄 进行中 |
| 2026 年 5 月 | 敲定内部 API 设计方案 | 🔄 即将开始 |
| 2026 年 5 月 | PyCon US 冲刺开发 | 🔄 即将开始 |
| 2026 年 6 月 | 开始撰写 PEP | ⏳ 计划中 |
| 2026 年 7 月 | PEP 草案提交社区讨论 | ⏳ 计划中 |
| 2027 年 5 月 | Python 3.16 beta(首个 Rust 模块上线) | ⏳ 计划中 |
| 2027 年 10 月 | Python 3.16 正式发布 | ⏳ 计划中 |
8.2 如何参与贡献
# 1. 加入 Discord 社区
# https://discord.gg/rust-for-cpython
# 2. 参加每周会议
# 每周一 12:00 PM PDT(北京时间每周二凌晨 3:00)
# 3. 查看待处理的 API 设计 issue
# GitHub: python/cpython 仓库,标签 api-design
# 4. 提交你的第一个 Rust 模块提案
# 格式参考:PEP 模板,包含以下部分:
# - 模块名称和当前 C 实现的 LOC
# - Rust 重写的预期收益(安全 + 性能)
# - FFI 桥接层的设计方案
# - 测试策略
# - 性能基准对比
8.3 对 Python 开发者的实际影响时间表
| 时间段 | 影响 | 你需要做什么 |
|---|---|---|
| 2026-2027 | 无感知 | 不需要做任何事,现有代码不受影响 |
| 2027-2028 | 首个 Rust 模块在 3.16 中上线 | 如果你的代码依赖 CPython 内部实现细节,关注 changelog |
| 2028-2030 | 更多模块逐步 Rust 化 | 可选:学习 PyO3,为你的包编写 Rust 扩展 |
| 2030+ | Rust 成为 CPython 的"第一公民" | Rust 技能成为 Python 开发者的加分项 |
九、深度思考:为什么这不是"又一个语言战争"
9.1 这不是 Rust vs C
Rust for CPython 项目不是在说"C 不好,Rust 更好"——它说的是"C 不好的一面(内存安全、构建复杂性)可以用 Rust 系统性地解决,同时保持 C API 的完全兼容"。Python 生态的 40 万+ PyPI 包中,绝大多数依赖 C 扩展——这些包不需要重写,不需要迁移,它们会继续正常工作。Rust 化是 CPython 内部的实现细节变化,不是 API 变化。
9.2 这是"渐进式类型安全"的成功模式
TypeScript 证明了"在一个动态类型语言上加类型系统可以渐进进行"。Rust for CPython 在做同样的事——在一个内存不安全的运行时上加内存安全保证,逐步替换,不中断。这个模式可能会影响其他语言:
- Ruby 社区已经在讨论类似方案
- PHP 8 的 JIT 和类型系统强化也是同一方向
- WebAssembly 的内存安全模型也是同一思路
9.3 对开发者就业市场的影响
Rust 的招聘市场将从"系统编程"(OS、嵌入式、网络基础设施)扩展到"Python 生态基础设施"(NumPy、Pandas、CPython 本身)。这不是推测——NumPy 已经在用 Rust 重写部分核心计算,Polars 已经是一个纯 Rust 的 DataFrame 库。如果你是 Python 开发者,现在学习 Rust 的投资回报率可能是有史以来最高的。
总结
Rust for CPython 项目是 Python 历史上最深刻的底层变革之一。它不是在做"语言替换"——它是在做"安全升级",用编译期的类型安全保证替换运行时的不确定行为。
从技术角度看,这件事的难度不在于"Rust 能不能做 Python 的底层"——答案显然是能——而在于"如何在一个 60 万行 C 代码、40 万+ 第三方包的生态中,逐模块地、不中断地替换实现"。FFI 边界设计、构建系统集成、渐进式迁移策略,每一个环节都需要精密的工程考量。
从生态角度看,Rust 进入 CPython 的意义远超技术本身——它标志着脚本语言社区正式承认"内存安全不是可选项",也标志着 Rust 从"系统编程语言"正式进入"高级语言基础设施"的角色。
2027 年 5 月,当 Python 3.16 beta 发布时,你 import itertools 的那一刻,底层可能就是 Rust 在运行。你的代码不会有任何变化,但你的 Python 进程会更安全、更快、更可靠。这就是这场革命的精髓——进步不需要你察觉,但它确实在发生。
参考来源
- Rust for CPython Progress Update April 2026 - Python Insider (blog.python.org/2026/04/rust-for-cpython-2026-04/)
- PyO3 官方文档 (pyo3.rs)
- Python C API 参考文档 (docs.python.org/3/c-api/)
- Rust for CPython GitHub 仓库 (github.com/ArcWindy/cpython-rust)
- Python 潮流周刊 #146:CPython 引入 Rust 的进展