章节目录

函数、模块、可见性与代码组织

本章目标

这一章学习如何用函数表达动作,用模块表达边界,用可见性控制外部能调用什么。你会理解为什么 RinBlog 把 Markdown 教材站放进 docs 模块,而不是继续塞进 main.rs

它是什么

函数是一段有名字、可复用的逻辑。模块是一组相关函数、类型和常量的命名空间。可见性决定这些名字能被谁使用。

fn add(left: i32, right: i32) -> i32 {
    left + right
}

这段代码定义了一个函数,接收两个 i32,返回一个 i32

为什么需要

没有函数,代码只能从上到下堆叠。没有模块,项目会变成一个巨大命名空间。没有可见性,任何地方都能调用任何内部细节。

好的组织方式会让问题更小:

  • route_inner 只关心“路径到处理函数”。
  • docs::render_docs_response 只关心“文档路径到文档页面”。
  • Markdown 渲染细节留在 docs 模块内部。

这样以后替换 Markdown 样式,不需要理解文章发布逻辑。

怎么使用

函数签名由三部分组成:

fn render_title(title: &str) -> String {
    format!("<h1>{title}</h1>")
}
  • 名字:render_title
  • 参数:title: &str
  • 返回值:String

模块中对外开放函数:

pub(crate) fn render_docs_response(path: &str) -> std::io::Result<Response> {
    // ...
}

pub(crate) 表示整个 crate 内可见,但 crate 外不可见。对当前应用来说,这比完全 pub 更保守。

逐行解释

pub(crate) fn is_docs_path(path: &str) -> bool {
    path == "/docs" || path == "/docs/" || path.starts_with("/docs/")
}
  • pub(crate):允许 crate 内其他模块调用。
  • fn is_docs_path:定义函数,名字表达“判断是不是文档路径”。
  • path: &str:接收一个字符串切片引用,不取得字符串所有权。
  • -> bool:返回布尔值。
  • path == "/docs":匹配文档首页。
  • path == "/docs/":匹配带尾斜杠的首页。
  • path.starts_with("/docs/"):匹配具体章节。
  • || 表示逻辑或,任意条件为真就返回真。

常见坑

  • 函数体最后一个表达式没有分号时,它会成为返回值。
  • 写了分号会把表达式变成语句,返回 ()
  • pub 不是越多越好。公开越多,后续越难修改。
  • 模块拆分后要注意依赖方向:底层模块不应该随便调用入口层。

练习

  1. 写一个 fn is_success(status: u16) -> bool
  2. 写一个 fn status_reason(status: u16) -> &'static str
  3. 新建模块 http.rs,把这两个函数放进去。
  4. 只公开你真正需要从外部调用的函数。

造轮子任务

造一个迷你路由判断器:写三个函数 is_homeis_postis_docs,分别判断路径是否属于首页、文章、文档。然后写一个 route_name(path) 返回路由名称。

小结

函数让代码可以命名,模块让能力可以分区,可见性让内部细节不被滥用。工程架构不是一开始就设计成大图,而是从这些小边界逐步长出来。