综合实战:设计一个小型 Rust 服务
本章目标
最后一章把整本书收束成一个实战蓝图。你会设计一个小型 Rust 服务,从需求、类型、路由、存储、并发、测试到演进路线形成完整工程思维。
它是什么
一个小型服务可以从这几个模块开始:
main:启动和配置。http:请求、响应、路由。docs:文档读取和渲染。posts:文章领域逻辑。storage:文件或数据库访问。templates:HTML 输出。
这不是固定答案,而是一种边界思路。
为什么需要
当功能从一个页面变成十个页面,问题不再是“怎么写一个函数”,而是“变化应该放在哪里”。工程架构的目标是降低修改成本:
- 新增路由不影响存储。
- 替换 Markdown 渲染不影响博客发布。
- 更换文件存储不影响 HTML 模板。
- 添加测试不用启动真实浏览器。
怎么使用
设计服务时,按这个顺序推进:
- 写清楚用户动作。
- 把动作变成路由。
- 把路由输入变成类型。
- 把核心概念建模成结构体和枚举。
- 决定错误在哪里处理。
- 写最小测试保护行为。
- 最后再考虑性能和并发。
不要一开始就搭巨大目录。先让边界在代码里自然出现。
逐行解释
enum AppError {
NotFound,
BadRequest(String),
Io(std::io::Error),
}
type AppResult<T> = Result<T, AppError>;
enum AppError定义应用级错误。NotFound表示资源不存在,不需要额外数据。BadRequest(String)表示客户端请求有问题,并携带说明。Io(std::io::Error)包装底层 IO 错误。type AppResult<T>创建类型别名,减少函数签名重复。Result<T, AppError>表示成功返回T,失败返回应用错误。
这种设计能把“底层失败”和“HTTP 应该怎么响应”分开。
常见坑
- 不要把 HTTP 状态码散落到所有业务函数里,最好在边界统一转换。
- 不要让存储层返回 HTML。存储层只返回数据或错误。
- 不要让模板层直接读文件。模板层只负责展示。
- 不要提前抽象所有东西。抽象应该来自重复和边界,而不是想象。
练习
- 为 RinBlog 画一个模块图。
- 设计
AppError,列出至少四种错误。 - 设计
Route枚举,覆盖首页、文章、文档和 404。 - 写一个表格,说明每个模块能依赖谁,不能依赖谁。
造轮子任务
完成一个“迷你服务架构草图”:不用实现全部功能,但要写出 Route、AppError、Post、DocChapter、Storage trait 的定义。然后说明每个类型负责什么,为什么这样分。
小结
从零基础到工程架构,真正的成长不是记住更多 API,而是能把问题拆成清晰边界。Rust 会不断要求你说清楚所有权、借用、错误和类型;当你习惯这种清晰,工程设计也会随之变得稳固。