通过十个项目学习Rust:从命令行工具到推理服务
十个Rust项目的渐进式学习阶梯,从用于扫描日志的命令行工具到Redis克隆版、shell及推理服务,以及每个项目分别能帮助我们理解内存、并发和故障处理方面的知识。
来源: 10 Rust Projects That Will Actually Make You Better at Programming in 2026
许多开始学习 Rust 的开发者会花费数周时间研究语法和教程,但一旦尝试构建实际项目就陷入停滞。仅仅记住关键字并不等同于理解为什么借用检查器会拒绝你的设计、异步任务如何共享状态,或是当套接字在请求过程中关闭时应该发生什么。跨越这一差距的可靠方法就是构建那些迫使你面对这些问题的项目。本指南按难度递增顺序列出了十个此类项目,解释了每个项目要求你学习的内容,并介绍了将完成的项目转化为真正技能所需的习惯(迭代、测试、基准测试以及有节制地运用人工智能)。
为何项目学习比死记语法更有效
Rust被广泛应用于系统软件、云基础设施、开发者工具、安全产品、区块链平台以及人工智能基础设施的某些部分。正是这种广泛的用途使其具有很高的学习价值,但同时也容易导致人们仅停留在表面了解。你或许可以记住所有这些关键词:
let
mut
fn
struct
enum
trait
impl
但当真正的程序需要在多个线程间共享可变状态时,你仍可能不知所措。最有用的学习项目未必规模庞大,而是那些能让你理解该语言为何如此设计的项目,因为它们会让你面临那种用简单方法既无法编译也无法应对真实输入的情况。
1. 一个能解决实际问题的命令行工具
从小处着手,从那些你真正会用到的工具开始。合适的候选工具包括日志分析器、文件搜索工具、JSON格式化工具、CSV处理工具、密码生成器、目录扫描工具或Markdown转换工具。
例如,日志扫描工具可以这样使用:
$ logscan app.log --level error
然后输出摘要以及对应的行内容(这里第一条结果行与摘要一起显示):
Found 42 errors12:31:04 Database connection failed
12:31:09 Request timeout
12:31:15 Authentication service unavailable
即便是个规模如此小的工具,也需要用到大量语言特性:
- 解析命令行参数
- 读取文件并处理I/O错误
- 使用
String与&str - 通过结构体和枚举对数据进行建模
- 利用
Result和?传递错误信息 - 将代码组织成模块
- 编写单元测试
同样重要的是,它的体积小到足以完成。将一个完整的产品交付出去本身就是一项技能,而一个做完成的微型工具比一个半途而废的庞大项目能带来更多收获。
2. 无需框架的最小化HTTP服务器
在动用Web框架之前,先基于TCP编写一个最基础的HTTP服务器。你最终实现的请求路径大致如下:
Client
↓
TCP connection
↓
HTTP parser
↓
Router
↓
Handler
↓
Response
在这个过程中,你会接触到TCP和套接字、HTTP消息的结构、请求解析、响应生成、缓冲区管理、各处理阶段之间数据的所有权问题,以及如何同时处理多个连接。该服务器无需达到生产环境标准,理解其原理才是关键。一旦你亲手完成了这些,像Axum或Actix Web这样的框架就不再显得神奇,因为你会清楚它们为你分担了哪些工作。
3. 基于真实数据库的REST API
接下来,让我们更接近真正的后端系统。任务管理API是一个不错的切入点,它包含常见的资源路由:
POST /tasks
GET /tasks
GET /tasks/:id
PUT /tasks/:id
DELETE /tasks/:id
随后再添加PostgreSQL数据库、身份验证机制、数据校验功能、结构化的错误响应、日志记录、配置管理以及集成测试。正是在这个阶段,各种编程语言的特性开始组合成完整的架构,同时也会出现一些更为重要的问题:某段逻辑应该由哪一层负责处理?当数据库不可用时API应如何响应?客户端应当收到怎样的状态码和响应内容?错误信息该如何从存储层传递到HTTP响应中?不同环境下的配置又应有何差异?这些设计问题远比任何单一的语法规则更为重要。
4. 并发网页爬虫
现在提高难度。一个一次获取多个页面的爬虫会从起点像这样向外扩展:
┌── URL 1
│
Start ───────┼── URL 2
│
├── URL 3
│
└── URL 4
为并行度设置明确的上限:
Maximum concurrent requests: 10
这一限制迫使你理解并发机制,而非只是创建任务并寄希望于最佳结果。你需要使用异步函数与任务、通道、共享状态及其所需的同步机制、超时处理、重试策略以及速率限制。此外还会遇到顺序程序中不会出现的问题,比如两个任务同时访问同一个URL,或者某个慢速主机拖累其他任务。正因会遇到这些问题,这个项目才值得去实现。
5. 键值存储
构建一个小型数据库是最有收获的学习项目之一。可以从三个命令开始:
SET name Rothy
GET name
DELETE name
接着通过持久化到磁盘、索引构建、序列化处理、键过期机制、事务支持以及安全的并发访问功能来进一步完善它。你并非要与 Redis 竞争,而是试图理解存储引擎的运作原理。这个项目能够锻炼数据结构、文件 I/O 操作、内存布局、序列化格式、并发处理能力、持久性保障以及性能优化方面的技能。正是在这里,Rust 的所有权模型才不再显得碍手碍脚,反而开始发挥实际作用——因为你现在需要管理那些程序中多个部分都可能访问的长期存在的状态。
6. 一个微型 Redis 服务器
如果你对键值存储感兴趣,可以将其改造成一个使用类似 Redis 协议的小型网络服务器:
SET user:1 Rothy
GET user:1
DEL user:1
并支持基于时间的过期功能:
EXPIRE user:1 60
随着时间的推移,你可以添加来自多个客户端的TCP连接、命令解析、并发请求处理、内存存储、数据持久化、过期机制以及性能测试功能。该项目融合了网络技术、数据结构、并发处理和系统设计,迫使你思考各种权衡因素。服务器应该优先优化延迟、吞吐量、内存占用还是数据持久性?很少有唯一的正确答案,学会有意识地做出选择并解释其理由,是这份清单中最具实用价值的经验之一。
7. 带有工作线程的任务队列
任务队列是后端系统的绝佳实践方式。其基本流程如下:
Producer
↓
Job Queue
↓
Workers
↓
Results
任务代表具体的工作单元,例如:
send_email
generate_report
process_image
run_test
首先从那些不断获取下一项任务并执行它的工作者开始。接着再添加多个工作者、重试机制、任务状态跟踪、超时处理、故障处理以及优雅关闭功能,直到设计呈现如下形态:
Producer
↓
┌───────────────┐
│ Job Queue │
└───────────────┘
↓ ↓ ↓
Worker Worker Worker
核心要点在于,软件设计不能只考虑正常流程。任务队列能让开发者决定当任务在执行过程中失败、工作者崩溃,或是系统在任务尚未完成时被要求停止时应如何处理。针对这些情况的设计才能将演示版本与真正可用的系统区分开来。
8. 日志聚合器
对于更接近实际生产环境的需求,可以构建一个小型日志收集服务。多个应用程序会将日志发送到这个收集器,由其进行存储:
Application A ──┐
Application B ──┼──> Log Collector ──> Storage
Application C ──┘
该收集器可以通过HTTP或原始TCP接收日志,并提供一个查询API:
GET /logs?level=error
GET /logs?service=payment
GET /logs?from=...
这项工作涉及网络通信、序列化、并发数据导入、过滤、时间戳处理、存储结构设计、性能优化以及可观测性等方面。这也是了解服务在负载下的表现情况的绝佳机会:当数据导入速度超过存储能力时会发生什么、内存缓冲需要多少资源,以及随着数据量增加查询速度如何变化。
9. 一个简易的 Unix shell
这个项目操作起来有些复杂,正因如此它才具有实用价值。可以先从运行简单命令开始:
$ echo hello
hello
同时处理内置命令和外部程序(此处第二个命令的提示信息已与第一个命令的输出合并在一起):
$ pwd
/home/user$ ls
file1.txt
file2.txt
接着再添加命令执行、环境变量、管道、重定向、后台进程以及信号处理功能,最终实现诸如这样的数据处理流程:
cat access.log | grep ERROR | sort
此时你正在接近操作系统层面进行编程。你会学习进程的创建与管理方式、stdin、stdout和stderr之间的连接机制、环境变量的继承规则、管道如何连接进程,以及底层的操作系统API是什么。很少有项目能像这样清晰地解释在终端中按下回车键时实际发生了什么。
10. 一个小型AI推理服务
这个最终项目反映了当前许多新基础设施开发的重点方向。其目标并非训练模型,而是围绕已有的模型构建相应的服务:
Client
↓
Rust API
↓
Request Queue
↓
Inference Worker
↓
Model
↓
Response
一个实用的服务会接收推理请求、验证输入、将任务加入队列、并发处理这些请求、返回结果、记录延迟时间、设置超时机制,并暴露相关指标。真正的工程难点在于基础设施层面,而非模型本身。你可以尝试调整并发限制、请求批处理、缓存策略、访问控制机制、内存使用情况、延迟预算、重试机制以及可观测性设置。正是在这些方面,Rust在内存管理和并发控制方面的优势才与人工智能应用背后的系统直接相关。
专注深入开发一个项目,而非浅尝辄止地尝试十个
没有必要在这里构建所有项目,试图做到这一点通常是个错误。挑选一个项目,完成它,然后再不断改进。第一个里程碑可以是:
Version 1
Basic functionality
随后是逐个添加新功能的版本(此列表中将箭头与版本标签放在一起显示):
↓Version 2
Better error handling ↓Version 3
Tests ↓Version 4
Concurrency ↓Version 5
Persistence ↓Version 6
Benchmarking ↓Version 7
Production-style monitoring
大部分学习内容都发生在那个改进循环中。每个版本都会迫使你重新审视之前的决策,而在新要求下重构自己的代码所带来的收获,远比从零开始另一个项目要多。
这些项目训练你提出的问题
学习 Rust 并非真正目标,真正的目标是学会思考软件问题。上述项目会不断让你面对同一类问题:
- 内存管理:谁拥有这些数据,它们的生命周期是多久?
- 并发问题:如果两个任务同时操作这些数据会怎样?
- 错误处理:当该操作失败时应该怎么做?
- 网络通信:如果客户端在传输过程中断开连接会怎样?
这些问题在任何编程语言中都存在。Rust只是让你更早地面对它们,通常是在编译时,而非在程序运行时。
建议的学习路径
如果你是从零开始,建议循序渐进,而非直接跳到最难的阶段:
- 初学者:使用命令行工具,学习语言基础和错误处理。
- 初学者到中级:使用HTTP服务器,了解网络及更低层次的概念。
- 中级:学习REST API,掌握真正的后端架构。
- 中级:编写并发爬虫,学习异步代码与并发机制。
- 中级到高级:使用键值存储,了解数据存储与状态管理。
从第一天就开始测试
不要等到最后才进行测试。对于每个项目,都要覆盖正常行为、无效输入、空输入、边界条件、并发操作、故障路径、超时以及恢复机制。如果某个接口需要用户ID,仅进行这样的正常流程测试是不够的:
GET /users/123
还需测试零值、负数、非数值类型、过大的ID以及缺失的参数情况:
GET /users/0
GET /users/-1
GET /users/abc
GET /users/999999999
GET /users/
这就是编程与测试融合为一门学科的地方。只有在所有情况都正常时才能运行的程序还算不上完成品。Rust内置的cargo test功能使得将这类测试用例与代码一起保存变得十分便捷。
通过基准测试而非假设来判断
Rust以速度快著称,因此应当通过实际测量而非主观断言来验证这一点。对服务器进行的负载测试可能会生成类似以下的报告;这些数值仅为示例,实际结果完全取决于硬件条件和工作负荷:
Requests: 100,000
Average: 4.2 ms
P95: 7.1 ms
P99: 11.4 ms
Throughput: 23,800 req/s
先记录基准值,再修改某个参数,之后再次进行测量。要注意P95和P99这样的尾部延迟指标,而不仅仅是平均值,因为竞争条件和内存分配问题通常会体现在这些数值上。养成在修改前后进行测量的习惯,才能让优化工作基于事实而非假设。
在学习过程中使用AI助手
现在的AI编程工具能够为你生成大量的Rust代码:结构体、API处理函数、测试用例、数据库查询语句、错误类型以及文档。这些确实很有用,但如果项目的目标是提升编程能力,完全依赖工具思考反而违背了初衷。更好的工作流程是先自己设计解决方案并实现它,然后再请助手帮忙审查,比如检查所有权问题、并发错误、错误处理漏洞以及不必要的内存分配。这样的审查能让你学到东西,而直接要求检查整个应用程序则无法达到此目的。关键不是避免使用AI,而是要在不放弃学习的前提下加以利用。
完整套件涵盖的内容
只要认真完成其中三到四个项目,就能让你接触到广泛的软件工程知识:
Rust
│
├── Ownership
├── Borrowing
├── Error handling
├── Traits
├── Generics
│
├── Networking
├── Concurrency
├── Async programming
│
├── Databases
├── APIs
├── Operating systems
│
├── Testing
├── Benchmarking
├── Observability
│
└── System design
这样的广度远比多完成一个教程更有价值。
关键要点
- 选择那些能迫使你面对所有权、并发性和故障处理问题,而不仅仅是语法问题的项目。
- 从能够完成的小项目开始,逐步过渡到网络、存储和操作系统层面的工作。
- 对一个项目进行多次迭代,而非同时启动多个项目。
- 从一开始就测试边界情况和故障路径,并对每一项宣称的改进进行性能基准测试。
- 可以让人工智能帮你审查设计和代码,但设计工作必须自己完成。
- 最好的下一个项目应该是能让你理解当前还不懂的知识的那种:从小处着手,拆解它、测试它、分析它的性能、修复问题,然后再增加难度。