章节目录

综合实战:设计一个小型 Rust 服务

本章目标

最后一章把整本书收束成一个实战蓝图。你会设计一个小型 Rust 服务,从需求、类型、路由、存储、并发、测试到演进路线形成完整工程思维。

它是什么

一个小型服务可以从这几个模块开始:

  • main:启动和配置。
  • http:请求、响应、路由。
  • docs:文档读取和渲染。
  • posts:文章领域逻辑。
  • storage:文件或数据库访问。
  • templates:HTML 输出。

这不是固定答案,而是一种边界思路。

为什么需要

当功能从一个页面变成十个页面,问题不再是“怎么写一个函数”,而是“变化应该放在哪里”。工程架构的目标是降低修改成本:

  • 新增路由不影响存储。
  • 替换 Markdown 渲染不影响博客发布。
  • 更换文件存储不影响 HTML 模板。
  • 添加测试不用启动真实浏览器。

怎么使用

设计服务时,按这个顺序推进:

  1. 写清楚用户动作。
  2. 把动作变成路由。
  3. 把路由输入变成类型。
  4. 把核心概念建模成结构体和枚举。
  5. 决定错误在哪里处理。
  6. 写最小测试保护行为。
  7. 最后再考虑性能和并发。

不要一开始就搭巨大目录。先让边界在代码里自然出现。

逐行解释

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。存储层只返回数据或错误。
  • 不要让模板层直接读文件。模板层只负责展示。
  • 不要提前抽象所有东西。抽象应该来自重复和边界,而不是想象。

练习

  1. 为 RinBlog 画一个模块图。
  2. 设计 AppError,列出至少四种错误。
  3. 设计 Route 枚举,覆盖首页、文章、文档和 404。
  4. 写一个表格,说明每个模块能依赖谁,不能依赖谁。

造轮子任务

完成一个“迷你服务架构草图”:不用实现全部功能,但要写出 RouteAppErrorPostDocChapterStorage trait 的定义。然后说明每个类型负责什么,为什么这样分。

小结

从零基础到工程架构,真正的成长不是记住更多 API,而是能把问题拆成清晰边界。Rust 会不断要求你说清楚所有权、借用、错误和类型;当你习惯这种清晰,工程设计也会随之变得稳固。