章节目录

项目结构与 Rust 程序心智模型

本章目标

这一章让你看懂一个 Rust 项目从磁盘文件到运行程序的基本关系。你会知道 src/main.rs 为什么重要,模块如何扩展项目,以及当前 RinBlog 为什么先把多数代码放在一个入口文件里。

它是什么

Rust 项目通常有这样的结构:

rinblog/
  Cargo.toml
  Cargo.lock
  src/
    main.rs
  docs/
    00-preface.md
  • Cargo.toml 描述项目。
  • Cargo.lock 锁定依赖版本。
  • src/main.rs 是可执行程序入口。
  • docs/ 是本书新增的 Markdown 教材目录。

如果一个项目是库,入口通常是 src/lib.rs。当前 RinBlog 是可执行 Web 服务,因此入口是 main.rs

为什么需要

工程结构的目标不是把文件拆得很多,而是让变化有地方放。小项目放一个文件能降低学习成本,大项目必须拆模块,否则任何改动都会互相影响。

一个健康的 Rust 工程通常会逐步形成这些边界:

  • 入口层:启动服务、读取配置。
  • 路由层:根据请求路径分发。
  • 领域层:表达业务概念。
  • 存储层:读写文件或数据库。
  • 展示层:生成 HTML 或 JSON。

当前项目先保持简单,再通过 src/docs.rs 演示如何把文档站能力拆出去。

怎么使用

你可以按调用顺序阅读当前程序:

  1. main 启动监听。
  2. handle_connection 处理一个 TCP 连接。
  3. read_http_request 解析 HTTP 请求。
  4. route_inner 分发路径。
  5. 渲染函数生成 HTML。
  6. 存储函数读写 data/posts
  7. docs::render_docs_response 读取并渲染教材 Markdown。

读代码时先抓数据流:请求从哪里来,变成什么结构,最后怎么写回浏览器。

逐行解释

看一个最小模块声明:

mod docs;

fn main() {
    println!("server starts");
}
  • mod docs; 告诉编译器:当前模块有一个子模块叫 docs
  • 编译器会查找 src/docs.rssrc/docs/mod.rs
  • fn main() 仍然是程序入口。
  • println! 是输出宏,用来观察程序运行。

模块不是运行时加载的脚本,而是编译期组织代码的方式。mod docs; 不会“启动文档站”,它只是把 docs.rs 编进项目,让代码可以调用里面的函数。

常见坑

  • mod xxx; 要写在父模块中,文件存在但没有声明时不会自动参与编译。
  • 文件名通常用小写和下划线,不用驼峰。
  • pub 控制可见性。没有 pub 的函数默认只在当前模块及子模块内部使用。
  • 不要为了“架构感”过早拆很多文件。拆分应该服务于边界,而不是制造目录。

练习

  1. 新建 src/scratch.rs,写一个返回字符串的函数。
  2. main.rs 添加 mod scratch;
  3. main 调用这个函数并打印结果。
  4. 去掉函数的 pub,观察编译器如何提示可见性问题。

造轮子任务

写一个 notes 模块,里面定义 fn render_note(title: &str, body: &str) -> String,返回一段简单 HTML。然后从 main 调用它,理解“展示逻辑可以拆到模块里”。

小结

Rust 工程结构的第一原则是边界清楚。入口负责启动,模块负责组织能力,可见性负责控制依赖方向。后面的章节会不断回到这个心智模型:先让代码跑起来,再让边界长出来。