学 Rust 的第二个月:从能编译到能上线,中间差了什么

📅 2026/7/12 15:46:46
学 Rust 的第二个月:从能编译到能上线,中间差了什么
学 Rust 的第二个月从能编译到能上线中间差了什么一、第一个月的幻觉能编译就是学会了第一个月学完 The Book做了几个练习项目所有代码都能通过编译clippy 一个 warning 都没有。我当时有一种错觉——Rust 也不难嘛有了编译器手把手教我犯错也犯不了。第二个月我开始尝试写一个真正有可能给别人用的项目——一个带 HTTP API 的文件元数据提取工具。这个月我经历了四次大重构 无数次小修小补最后才拿出一个勉强可用的版本。回顾这个过程我把它总结成一个等式搞懂 Rust ≠ 能写 Rust ≠ 能写好 Rust ≠ 能发布 Rust 项目。flowchart LR A[第一阶段\n能编译] -- B[第二阶段\n能跑起来] B -- C[第三阶段\n能处理错误] C -- D[第四阶段\n能被别人用] A -.- A1[看懂 The Book\n过一遍 Rustlings] B -.- B1[会连接外部依赖\n会读写文件/网络] C -.- C1[理解 Result/Option\n错误类型设计\n日志和可观测性] D -.- D1[CLI 用户体验\n文档/CHANGELOG\nCI/CD 配置]二、我踩过的四个阶段的具体经历阶段 1能编译但经不起场景变化第一个月写的代码普遍有这种特点// 第一阶段这样写确实能编译通过 fn extract_metadata(path: str) - Metadata { let file std::fs::File::open(path).unwrap(); // 直接 unwrap let metadata file.metadata().unwrap(); // 再 unwrap Metadata { size: metadata.len(), // ... 其他字段 } }问题一目了然路径不存在怎么办权限不够怎么办这些在 The Book 的示例里不存在但真实场景天天要处理。阶段 2能跑起来但结构混乱第二版我开始把所有逻辑堆进main.rs代码长到 500 多行。每次加新功能都要在函数丛林里找该改哪。某个周末我想加个支持递归目录扫描的功能看了 20 分钟代码不知道从哪下手。这时候我意识到能跑 ≠ 能维护。KISS 原则的核心不是简单而是每个模块只做一件事。于是我开始拆分模块// 项目结构每个模块有清晰的职责边界 // src/ // main.rs ← 入口 CLI 参数解析 // metadata.rs ← 文件元数据提取逻辑 // format.rs ← 输出格式JSON / YAML / 表格 // error.rs ← 自定义错误类型 // scanner.rs ← 目录遍历逻辑抽出来后代码量没少但可读性好了太多。每次改一个功能只需要关注对应文件。阶段 3能跑 有模块但错误信息让人困惑拆分模块后我又遇到了新问题错误处理和日志。我之前的所有错误处理都是unwrap()加expect(出错了)。一旦有用户传了一个不存在的文件路径看到的输出是thread main panicked at src/metadata.rs:42:14: 出错了: No such file or directory (os error 2)这比什么都没写强但距离可发布还差太远。用户不需要看到线程 panic 信息他需要的是错误无法访问文件 /tmp/nonexistent.txt 原因文件不存在 建议请检查文件路径是否正确要实现这种效果需要引入自定义错误类型use std::path::PathBuf; use thiserror::Error; /// 自定义错误类型每个变体包含足够的上下文信息 #[derive(Error, Debug)] pub enum AppError { #[error(无法访问文件 {path}文件不存在)] FileNotFound { path: PathBuf, }, #[error(无法读取文件 {path}权限不足)] PermissionDenied { path: PathBuf, }, #[error(I/O 错误{source})] Io { #[from] source: std::io::Error, }, } // 使用时的体验 fn open_file(path: Path) - ResultFile, AppError { if !path.exists() { return Err(AppError::FileNotFound { path: path.to_path_buf(), }); } File::open(path).map_err(|e| { if e.kind() std::io::ErrorKind::PermissionDenied { AppError::PermissionDenied { path: path.to_path_buf(), } } else { AppError::Io { source: e } } }) }阶段 4能发布但别人用不起来解决了错误处理问题后我以为差不多了。结果发给朋友他第一句话就是怎么用文档呢我这才意识到一个开源项目至少需要README.md项目介绍 快速开始 API 文档链接命令行帮助--help输出清晰的使用说明错误信息对用户友好区分用户自己的问题和程序 bug安装方式至少提供cargo install或 GitHub Release 的二进制我的项目用clap实现了完整的命令行帮助use clap::Parser; /// 文件元数据提取工具——支持多种格式输出 #[derive(Parser, Debug)] #[command(name meta-extractor)] #[command(version 0.2.0)] #[command(about 提取文件元数据并输出为 JSON/YAML/表格)] struct Cli { /// 要分析的文件或目录路径 #[arg(required true)] path: String, /// 输出格式json, yaml, table #[arg(short, long, default_value table)] format: String, /// 是否递归扫描目录 #[arg(short, long, default_value_t false)] recursive: bool, }三、的自学方法项目驱动 刻意练习flowchart TD A[选一个真实需求的项目] -- B[只实现一个核心功能] B -- C[能跑起来后先给别人试用] C -- D[根据反馈加功能/改设计] D -- E[重构拆模块、加错误处理] E -- F[发布第一版] F -- G[持续迭代] G -- C我的方法不是按教科书顺序学而是项目驱动先想一个不超过 200 行能搞定的功能写出来给别人试收到反馈后修改在修改过程中自然接触到 Rust 的深层概念这样学的好处是每次接触新概念都有真实需求作为锚点不会觉得是在学抽象知识。四、给自学者的资源清单我在第二个月做了一件事把 Rust 的学习资源分了三类类型资源适合阶段入门The Book Rustlings第一个月进阶Rust by Example第二个月实战Jon Gjengset 的视频 crates.io 读源码第三个月起特别推荐读源码用cargo doc --open打开你用的 crate 的文档慢慢看 API 设计。这是培养工程感最快的方式。五、总结第二个月最大的收获是打破了一个幻觉编译器检查 ≠ 代码质量检查。Rust 编译器能保证内存安全但不能保证结构清晰不能保证错误处理好也不能保证用户体验好。从能编译到能上线中间差的不是 Rust 语法知识而是工程意识——包括模块拆分、错误处理设计、命令行体验优化以及把代码交到别人手里这件事带来的责任感。自学不可怕可怕的是满足于代码能跑。第二阶段的目标不是学更多语法而是学会让别人也能跑你写的代码。共勉。下一篇文章我会分享如何用 AI 辅助 Rust 代码结构化把 500 行的main.rs拆成干净的模块。欢迎在评论区分享你的 Rust 自学经历。