首页
/ 用 rustlings 系统掌握 Rust 模块系统:pub 可见性、use 路径导入与 pub use 重导出

用 rustlings 系统掌握 Rust 模块系统:pub 可见性、use 路径导入与 pub use 重导出

2026-09-04 17:00:36作者:彭桢灵Jeremy

本篇围绕 rustlings 仓库中 exercises/10_modules/ 模块章节(对应官方文档 exercises/10_modules/README.md)展开,介绍 Rust 模块系统(module system)的三个核心能力:默认私有与 pub 可见性控制、use/as 将模块路径引入作用域并重命名、以及通过 pub use 向外部暴露与内部结构不同的接口。你将跟随 modules1~modules3 三道练习题的完整代码,掌握如何在真实工程中组织代码可见性与命名空间。

模块章节定位与练习构成

exercises/10_modules/README.md 是 rustlings 课程中"模块"章节的导读,其核心目标是给出 Rust 模块系统的入门指引。在 rustlings 的练习体系中,该章节由三道源码文件组成,各自聚焦一个知识点:

练习文件 考察重点 官方提示(hint)
modules1.rs mod 与可见性:模块内函数默认私有,需用 pub 对外暴露 "Everything is private in Rust by default. But there's a keyword we can use to make something public!"(Rust 中一切默认私有,但有一个关键字可以把它变为公开)
modules2.rs use ... as 重命名 + pub use 重导出 提示要补齐两条 use 语句,并找到"两个常量都缺少的一个关键字"
modules3.rs 用嵌套路径一行引入标准库模块 提示 UNIX_EPOCHSystemTime 声明在 std::time 模块中,可用嵌套路径一行引入

三道练习的 hint 文本集中维护在 rustlings-macros/info.toml[[exercises]] 配置段中,其中 modules1~modules3 均标记为 test = false,意味着 rustlings 只要求练习编译通过modules3 额外要求运行时不 panic),而不依赖单元测试断言。

modules1:模块边界与 pub 可见性

练习代码与报错

modules1.rs 构造了一个典型的"模块内部实现"场景——香肠工厂(sausage_factory 模块):

// TODO: Fix the compiler error about calling a private function.
mod sausage_factory {
    // Don't let anybody outside of this module see this!
    fn get_secret_recipe() -> String {
        String::from("Ginger")
    }

    fn make_sausage() {
        get_secret_recipe();
        println!("sausage!");
    }
}

fn main() {
    sausage_factory::make_sausage();
}

这段代码无法通过编译:main 中调用 sausage_factory::make_sausage() 时,make_sausage 没有 pub 修饰,属于私有项,模块外部(main 所在的根作用域)不可访问。这正是 Rust 的可见性规则——一切默认私有,且可见性按模块边界逐层生效:只有当前模块及其子模块能看到当前模块中的私有项。

修复方式与官方解法

对照 solutions/10_modules/modules1.rs,官方解法只改了一处:

mod sausage_factory {
    fn get_secret_recipe() -> String {
        String::from("Ginger")
    }

    // Added `pub` before `fn` to make the function accessible outside the module.
    pub fn make_sausage() {
        get_secret_recipe();
        println!("sausage!");
    }
}

fn main() {
    sausage_factory::make_sausage();
}

get_secret_recipe 保持私有,make_sausage 加上 pub。这个细节值得注意:修复的最小改动是只暴露必要的 APImake_sausage),而不必把"秘方"函数也公开。这体现了 Rust 模块设计的一个惯用做法——用模块边界封装内部实现,对外仅暴露最小公开面;调用方只能经由 make_sausage 间接触达 get_secret_recipe

modules2:use/as 引入路径与 pub use 重导出

练习代码

modules2.rs 要求模块对外呈现一套"与内部结构不同的接口"。内部结构是两个子模块 fruitsveggies,各自定义公开常量:

mod delicious_snacks {
    // TODO: Add the following two `use` statements after fixing them.
    // use self::fruits::PEAR as ???;
    // use self::veggies::CUCUMBER as ???;

    mod fruits {
        pub const PEAR: &str = "Pear";
        pub const APPLE: &str = "Apple";
    }

    mod veggies {
        pub const CUCUMBER: &str = "Cucumber";
        pub const CARROT: &str = "Carrot";
    }
}

fn main() {
    println!(
        "favorite snacks: {} and {}",
        delicious_snacks::fruit,
        delicious_snacks::veggie,
    );
}

注意 main 中期望的路径是 delicious_snacks::fruitdelicious_snacks::veggie——小写、无子模块前缀,与内部 fruits::PEARveggies::CUCUMBER 的结构完全不同。因此需要两件事:

  1. use ... as 把常量路径引入 delicious_snacks 作用域并重命名
  2. 让外部能沿 delicious_snacks::fruit 这个路径找到它们——即 use 本身还要公开。

修复方式:pub use 重导出

对照 solutions/10_modules/modules2.rs

mod delicious_snacks {
    // Added `pub` and used the expected alias after `as`.
    pub use self::fruits::PEAR as fruit;
    pub use self::veggies::CUCUMBER as veggie;

    mod fruits {
        pub const PEAR: &str = "Pear";
        pub const APPLE: &str = "Apple";
    }

    mod veggies {
        pub const CUCUMBER: &str = "Cucumber";
        pub const CARROT: &str = "Carrot";
    }
}

几个要点:

  • self:: 前缀:在模块内部以 self 指代当前模块本身,self::fruits::PEAR 即"本模块下 fruits 子模块的 PEAR"。
  • as 重命名use self::fruits::PEAR as fruit; 创建别名 fruit,使 maindelicious_snacks::fruit 的写法成立。
  • pub use 重导出:这是官方 hint 中所说的"两个常量都缺少的一个关键字"。若只写 use(不加 pub),别名仅在当前模块内部可见,main 依然无法访问;加上 pub 后,delicious_snacks 模块就对外重导出了 fruit/veggie 两个名字,而内部真实结构(fruitsveggies 子模块及常量)仍然保持私有。这正是"模块作为接口边界"的体现——外部使用者只看到 delicious_snacks::fruit,看不到其内部组织。

modules3:一行引入标准库模块(嵌套路径)

modules3.rs 的场景是计算当前时间距 UNIX 纪元的秒数,代码中直接使用了 SystemTimeUNIX_EPOCH,但尚未引入作用域:

// TODO: Bring `SystemTime` and `UNIX_EPOCH` from the `std::time` module into
// your scope. Bonus style points if you can do it with one line!
// use ???;

fn main() {
    match SystemTime::now().duration_since(UNIX_EPOCH) {
        Ok(n) => println!("1970-01-01 00:00:00 UTC was {} seconds ago!", n.as_secs()),
        Err(_) => panic!("SystemTime before UNIX EPOCH!"),
    }
}

官方解法 solutions/10_modules/modules3.rs 用一条嵌套路径语句同时引入两个名字,拿到题目所说的"Bonus style points":

use std::time::{SystemTime, UNIX_EPOCH};

这里 use 的"路径"不限于自己项目的模块树,同样适用于标准库(std::time)。大括号语法 use 路径::{A, B}; 允许在一条语句中导入同一模块下的多个项,比写两行 use std::time::SystemTime; use std::time::UNIX_EPOCH; 更简洁。test = false 的校验方式下,该练习的通过标准是:编译通过,且运行 main 正常打印(若时钟早于 UNIX 纪元会触发 panic,正常系统下不会)。

rustlings 如何组织与校验这些练习

从仓库结构看,模块章节的练习文件、解法文件与校验配置是分离维护的:

{ name = "modules1", path = "../exercises/10_modules/modules1.rs" },
{ name = "modules1_sol", path = "../solutions/10_modules/modules1.rs" },
{ name = "modules2", path = "../exercises/10_modules/modules2.rs" },
{ name = "modules2_sol", path = "../solutions/10_modules/modules2.rs" },
{ name = "modules3", path = "../exercises/10_modules/modules3.rs" },
{ name = "modules3_sol", path = "../solutions/10_modules/modules3.rs" },

这说明 rustlings 的校验流程是把每个 .rs 练习作为独立编译单元进行编译(_sol 版本则用于验证解法可编译通过);

  • 每个练习的元数据(目录、是否含测试、hint 文本)记录在 rustlings-macros/info.toml 中,模块三道题均为 test = false,即纯编译校验,与 tests/test_exercises/info.toml 中带测试用例(如 test_failure)的练习形成对照;
  • 练习文件顶部的 // TODO: 注释即为需要修改的位置,官方 hint 会给出方向而不直接给答案(如 modules1 的 hint 只提示"有一个关键字可以让项变公开")。

学习者通常通过 cargo run(rustlings CLI)或 cargo dev 驱动开发循环来完成本章节:阅读报错 → 按 hint 方向修改 → 编译/运行通过即过关。

小结:模块三要素速查

语法 作用 本章节出处
mod name { ... } 声明模块,划定作用域边界,内部项默认私有 modules1.rs
pub 使项(函数/常量/模块)对模块外部可见 modules1 解法modules2 解法
use 路径 as 别名; 引入模块路径并在新作用域重命名 modules2 解法
pub use ...; 重导出名字,对外暴露与内部结构不同的接口 modules2 解法
use a::b::{X, Y}; 嵌套路径,一行引入多个标准库项 modules3 解法

完成本章后,建议继续回顾 Rust 官方书籍 The Book 第 7 章("Managing Growing Projects with Packages, Crates, and Modules")中"用 use 关键字把路径引入作用域"一节的 pub use 重导出小节,以补全本章练习涉及的完整知识背景。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
docsdocs
暂无描述
Markdown
889
5.78 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384
flutter_flutterflutter_flutter
本仓库是 Flutter SDK 与 Flutter Engine 的 OpenHarmony 适配版本,由 CPF-Flutter 团队维护。开发者可使用熟悉的 Flutter 技术栈开发 OpenHarmony 应用,3.35.7 及以后的适配版本可基于本仓库源码构建支持 OpenHarmony 的 Flutter Engine。
Dart
1.17 K
341