函数、模块、可见性与代码组织
本章目标
这一章学习如何用函数表达动作,用模块表达边界,用可见性控制外部能调用什么。你会理解为什么 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不是越多越好。公开越多,后续越难修改。- 模块拆分后要注意依赖方向:底层模块不应该随便调用入口层。
练习
- 写一个
fn is_success(status: u16) -> bool。 - 写一个
fn status_reason(status: u16) -> &'static str。 - 新建模块
http.rs,把这两个函数放进去。 - 只公开你真正需要从外部调用的函数。
造轮子任务
造一个迷你路由判断器:写三个函数 is_home、is_post、is_docs,分别判断路径是否属于首页、文章、文档。然后写一个 route_name(path) 返回路由名称。
小结
函数让代码可以命名,模块让能力可以分区,可见性让内部细节不被滥用。工程架构不是一开始就设计成大图,而是从这些小边界逐步长出来。