大家好,我写了一个用 Rust 从零构建 Lisp 解释器的实战教程,希望和大家分享。
项目地址:https://github.com/lisering/lisp-rs
为什么做这个项目
学 Rust 的过程中,最大的痛点是:教程很多,但完整的实战项目很少。写一个解释器是学 Rust 所有权和类型系统的绝佳练习——你会遇到大量真实的借用、克隆、智能指针问题,而且每一步都有明确的正确性验证(测试)。
项目特点
1. 零外部依赖
Cargo.toml 里没有任何依赖:
[package]
name = "lisp-rs"
version = "0.1.0"
edition = "2024"
[dependencies]
连哈希器都是自己手写的 FxHasher(用黄金比例常数做快速搅拌),字符串驻留(string interning)也是从头实现。这意味着学习者可以理解每一行代码,没有任何黑盒。
2. 核心数据结构
整个解释器的核心就是一个 enum + Rc<RefCell>:
pub enum LispExp {
Number(f64),
Symbol(u64), // 驻留后的整数 ID,不是 String
List(Vec<LispExp>),
Func(fn(&[LispExp]) -> Result<LispExp, LispErr>),
Lambda(Box<LispLambda>),
Macro(Box<LispLambda>),
Bool(bool),
Nil,
String(String),
}
pub struct LispLambda {
pub params: Vec<u64>,
pub rest: Option<u64>,
pub body: Box<LispExp>,
pub env: Rc<RefCell<LispEnv>>, // 闭包捕获环境
}
如果你正在学 Rust 的 Rc、RefCell、Box,这个项目是一个完整的实战案例。
3. 实现了哪些特性
| 特性 | 步骤 | Rust 知识点 |
|---|---|---|
| 词法分析 | 9-11 | Vec、迭代器 |
| 解析器 | 12-15 | 递归、Result 错误传播 |
| 变量绑定 | 16-19 | HashMap、自定义 hasher |
| 闭包 | 37 | Rc<RefCell> 共享所有权 |
| 尾调用优化 | 39 | 循环替代递归(蹦床) |
| 宏系统 | 71-71e | 模式匹配、代码生成 |
| 字符串驻留 | 40 | OnceLock、RwLock、全局静态 |
4. TCO 蹦床实现
用循环替代递归,100 万次迭代不会栈溢出:
pub fn eval(exp: &LispExp, env: &mut LispEnv) -> Result<LispExp, LispErr> {
let mut current_exp = exp.clone();
let mut current_env = std::mem::take(env);
loop {
match ¤t_exp {
// ... 自求值类型直接返回
LispExp::List(elements) => {
// if 的尾位置优化:不递归,直接改 current_exp 继续 loop
if *sym == p.if_sym {
let cond = eval(&elements[1], &mut current_env)?;
current_exp = if is_true { elements[2] } else { elements[3] };
continue; // ← 蹦床:不递归,回到 loop 顶部
}
}
}
}
}
5. 文档投入
- 中文 README 约 8,000 行,英文版同样长度
- 44 张手绘 SVG 架构图(中英双语),覆盖流水线、AST 树、闭包、TCO 蹦床、RC 共享等概念
- 74 个步骤,每一步先讲要解决什么问题,再写代码
- 42 个测试用例,随时可以
cargo test验证
和其他教程的区别
| 教程 | 语言 | 区别 |
|---|---|---|
| Crafting Interpreters | Java/C | 它面向有经验程序员,做两遍(tree-walk + bytecode);本教程面向零基础,做一遍 tree-walk |
| mal (Make a Lisp) | 80+语言 | 它给测试用例但不解释 why;本教程每步先讲为什么 |
| SICP | Scheme | 它教编程思维,本教程教解释器实现 |
这不是什么
这是一个教学解释器——tree-walking eval,没有字节码编译器,没有 JIT。优化步骤(40-43)是为了演示这些技术怎么工作,不是为了性能。如果你想要生产级 Scheme,推荐 scheme-rs。
项目结构
src/
├── lib.rs # LispExp enum、LispLambda、Display trait
├── lexer.rs # 词法分析(字符串 → token)
├── parser.rs # 语法分析(token → AST)
├── interpreter.rs # 求值器(AST → 结果),约 1050 行
├── env.rs # 环境(HashMap + FxHasher + Rc<RefCell>)
├── interner.rs # 字符串驻留(OnceLock + RwLock)
└── main.rs # REPL 入口
总共约 1,658 行 Rust 代码。
欢迎反馈
- 如果你是 Rust 新手:从步骤 1 开始跟着走,每步都有
cargo test验证 - 如果你有经验:直接跳到步骤 37(闭包)和步骤 39(TCO)
- 欢迎提 issue 和 PR,特别是关于教学讲解的改进建议
1
共 0 条评论, 1 页
评论区
写评论还没有评论