GitHub
加载中...
加载GitHub贡献图...
✨ 欢迎来到桃源笔记
这里是记录生活、分享技术、探索世界的小天地。愿你在忙碌的生活中,也能找到属于自己的那片桃源~
标签
安全 宝库 笔记 编程实践 博客 触摸事件 低碳生活 调试 都市减压 独处 个人成长 个性化教育 更新日志 工具 工业4.0 工作生活平衡 公告 构建 孤独 观察 航天技术 合成生物学 环保 火星任务 基因编辑 计算器 技能 技术创新 加密货币 焦虑 教育 开源 科技趋势 科技与生活 可持续生活 量子计算 灵活办公 零浪费 浏览器API 留白艺术 慢生活哲学 命令行工具 内心成长 评论系统 前端 区块链 去中心化 人工智能 人文 人性思考 商业航天 商业应用 生活 生活方式 生活平衡 生活哲学 生态友好 生物技术 时间管理 实验室 实用方法 书单 数字化 数字极简主义 数字经济 数字孪生 数字伦理 数字转型 随笔 太空探索 太空殖民 体验优化 推荐 未来工作 未来经济 未来科技 未来学习 未来展望 物联网 咸鱼之王 小工具 效率 效率工具 协议 心理健康 心理健康工具 心灵治愈 性能优化 虚拟现实 医疗创新 移动端 音乐 游戏 元宇宙 远程工作 远程医疗 阅读 增强现实 正念 知识管理 智慧生活 智能制造 专注力 自我关怀 自我疗愈 自我探索 自我提升 Astro Canvas GitHub Obsidian Pagefind PKM Python RSS Rust Svelte Umami Web3
1334 字
7 分钟
我用 Rust 重写了一个命令行工具
TL;DR
把一个日常使用的 Python 脚本用 Rust 重写了,性能从 2.3s 提升到 0.04s,内存占用从 45MB 降到 3MB。这篇文章记录了整个迁移过程和踩过的坑。
起因:一个越来越慢的脚本
我有个 Python 脚本,用来批量处理 Markdown 文件:提取 frontmatter、统计字数、生成目录。最开始只有几十个文件时还挺快,但随着文章越来越多(现在 200+ 篇),每次运行都要等好几秒。
$ time python process.py处理完成:215 个文件
real 0m2.347suser 0m2.156ssys 0m0.187s虽然 2 秒不算太慢,但每次修改文章后都要跑一遍,一天下来要等好几分钟。更要命的是,这个脚本还会被 CI/CD 调用,拖慢了整个构建流程。
为什么选 Rust
最开始想过几个方案:
- 优化 Python 代码:试过用
multiprocessing,但 GIL 和进程开销反而更慢 - 换成 Go:写起来确实快,但二进制文件太大(15MB+)
- 试试 Rust:听说性能好,正好学习一下
最终选 Rust 的原因:
- 零成本抽象,性能接近 C
- 内存安全,不用担心段错误
- 生态成熟,有很多现成的库
- 编译后的二进制小(3MB 左右)
实现过程
1. 项目初始化
cargo new md-processor --bincd md-processor添加依赖(Cargo.toml):
[dependencies]walkdir = "2" # 遍历目录gray_matter = "0.2" # 解析 frontmatterregex = "1" # 正则匹配rayon = "1.7" # 并行处理serde = { version = "1", features = ["derive"] }serde_json = "1"2. 核心逻辑
Python 版本的核心代码:
def process_file(path): with open(path, 'r', encoding='utf-8') as f: content = f.read()
# 提取 frontmatter matter = frontmatter.loads(content)
# 统计字数 text = re.sub(r'[^\\u4e00-\\u9fa5a-zA-Z0-9]', '', matter.content) word_count = len(text)
return { 'path': path, 'title': matter.get('title', ''), 'words': word_count }
# 串行处理results = [process_file(f) for f in files]Rust 版本:
use rayon::prelude::*;use std::fs;
#[derive(Debug, Serialize)]struct FileInfo { path: String, title: String, words: usize,}
fn process_file(path: &Path) -> Result<FileInfo> { let content = fs::read_to_string(path)?;
// 解析 frontmatter let matter = gray_matter::Matter::<gray_matter::engine::YAML>::new(); let parsed = matter.parse(&content);
// 统计字数(只保留中英文和数字) let text: String = parsed.content .chars() .filter(|c| c.is_alphanumeric() || (*c >= '\\u{4e00}' && *c <= '\\u{9fa5}')) .collect();
Ok(FileInfo { path: path.display().to_string(), title: parsed.data .as_ref() .and_then(|d| d.get("title")) .and_then(|t| t.as_str()) .unwrap_or("") .to_string(), words: text.len(), })}
fn main() -> Result<()> { let files: Vec<_> = WalkDir::new("posts") .into_iter() .filter_map(|e| e.ok()) .filter(|e| e.path().extension() == Some(OsStr::new("md"))) .collect();
// 并行处理 let results: Vec<_> = files .par_iter() .filter_map(|entry| process_file(entry.path()).ok()) .collect();
println!("处理完成:{} 个文件", results.len()); Ok(())}3. 踩过的坑
坑1:字符串处理
Rust 的字符串是 UTF-8 编码,不能直接按索引访问。一开始写成:
let char = content[0]; // ❌ 编译错误正确做法:
let char = content.chars().next(); // ✅坑2:错误处理
Python 可以随便 try-except,Rust 必须显式处理每个 Result。一开始写了很多 unwrap(),结果遇到异常文件就 panic。
改用 ? 操作符和 filter_map 优雅处理:
let results: Vec<_> = files .par_iter() .filter_map(|entry| process_file(entry.path()).ok()) // 忽略错误 .collect();坑3:并行处理的开销
一开始直接用 rayon 并行,发现小文件反而变慢了。原因是线程创建和调度的开销。
解决方案:只在文件数量 > 50 时才并行:
let results = if files.len() > 50 { files.par_iter().filter_map(|e| process_file(e).ok()).collect()} else { files.iter().filter_map(|e| process_file(e).ok()).collect()};性能对比
基准测试
测试环境:
- CPU: AMD Ryzen 7 5800H
- RAM: 16GB
- 文件数:215 个 Markdown 文件
- 总大小:约 3.2MB
| 指标 | Python | Rust | 提升 |
|---|---|---|---|
| 执行时间 | 2.347s | 0.043s | 54x |
| 内存占用 | 45MB | 2.8MB | 16x |
| 二进制大小 | - | 3.1MB | - |
| 启动时间 | 0.18s | 0.001s | 180x |
为什么这么快?
- 编译优化:Rust 编译时做了大量优化(内联、循环展开等)
- 零拷贝:字符串处理时避免了不必要的内存分配
- 并行处理:
rayon自动利用多核 CPU - 无 GC:没有垃圾回收的停顿
实际收益
开发体验
优点:
- 编译器非常严格,很多 bug 在编译期就被发现
- 类型系统强大,重构很安全
- 性能确实快,用起来很爽
缺点:
- 学习曲线陡峭,所有权系统需要时间理解
- 编译时间长(首次编译 2 分钟+)
- 生态虽然成熟,但不如 Python 丰富
CI/CD 优化
之前构建流程:
拉取代码 (5s) → 安装依赖 (15s) → 处理文件 (2.3s) → 构建 (30s)总计:52.3s现在:
拉取代码 (5s) → 下载二进制 (1s) → 处理文件 (0.04s) → 构建 (30s)总计:36s每次构建节省 16 秒,一天跑 20 次就是 5 分钟。
经验总结
什么时候该用 Rust
适合:
- 性能敏感的工具(CLI、数据处理)
- 需要长期维护的项目
- 对内存占用有要求
- 需要跨平台分发
不适合:
- 快速原型开发
- 频繁变更需求的项目
- 团队没有 Rust 经验
- 简单的一次性脚本
给新手的建议
- 从小项目开始:不要一上来就重写大型项目
- 多看文档:Rust Book 和 Rust by Example 写得很好
- 善用编译器:错误信息很详细,认真读
- 拥抱所有权:不要和编译器对抗,理解它的设计哲学
- 利用生态:crates.io 上有很多优秀的库
后续计划
这次重写让我尝到了甜头,接下来打算:
- 把其他几个 Python 脚本也迁移过来
- 学习
async/await,处理网络请求 - 尝试用 Rust 写个 Web 服务(Axum 框架)
代码仓库
完整代码已开源:github.com/example/md-processor
欢迎 star 和提 issue!
更新记录:
- 2025-12-25:初版发布
- 2025-12-26:添加了错误处理优化
- 2025-12-27:修复了中文字符统计的 bug
部分信息可能已经过时




