Rust 的模块

  • 如何使用模块在文件和文件夹中组织代码
  • 模块成员的可见性
  • 在 crates.io 上发布代码

Rust 的模块化编程

Rust代码组织的基本概念

  • Package:包
  • Crate:箱
  • Module:模块

Package

  • 用于管理一个 or 多个 Crate,Package 里最少有一个 Crate
  • 使用 cargo new <name> 创建一个 Package 包含的文件:
    • 必须包含:cargo.toml
    • 默认情况 src/main.rs: 是与Package同名的二进制Crate的入口文件
    • 因此,现在我们有一个 Package 以及一个 Crate
    • 同样,create new 带上 —lib,那么 src/lib.rs 将是它的入口文件,且它是一个库 Crate
  • 如果src目录中同时包含 main.rs 和 lib.rs ,那么我们将在Package中同时得到一个二进制Crate和一个库Crate
    • 这在开发一些基础库的时候很有用
    • 例如:用Rust实现了一个函数,既希望这个函数作为库被别人使用,又想获得一个可以进行这个函数计算的命令行工具

Crate

  • 是 Rust 的最小编译单元
  • Crate 在一个范围内将相关功能组合在一起
  • 并通过最终编译生成一个二进制或者库文件
  • 一个 Crate 里有 0 或多个 Module

Module

  • 允许我们将 Crate 中的代码组织成独立的代码块,增强可读性和复用性
  • Module 还控制代码可见性:将代码分为公有和私有代码
    • 公有代码可以在项目外被使用
    • 私有代码项目内部的代码才可以访问
  • 定义一个模块最基本的方式是使用mod关键字 ```rust mod mod1 { pub mod mod2 {
    1. pub const MESSAGE: &str = "mod2 message";
    } }

fn main() { println!(“{}”, mod1::mod2::MESSAGE); }

  1. <a name="kiFOJ"></a>
  2. #
  3. <a name="FrEwp"></a>
  4. # 可见性
  5. - Rust 中模块内部的代码,结构体,函数等类型默认是私有的
  6. - 但是可以通过 pub 关键字来改变它们的可见性
  7. - 可以选择性地对外可见来隐藏模块内部的实现细节
  8. <a name="fn05I"></a>
  9. ## 常用的三种 pub 写法
  10. - pub:成员对模块可见
  11. - pub(self):成员对模块内的子模块可见
  12. - pub(crate):成员对整个 crate 可见
  13. - 不使用 pub 声明默认私有
  14. ```rust
  15. mod mod1 {
  16. // 不加 pub(self) 也可以在mod2::say里访问
  17. // 所以 pub(self) 其实是一个偏标记性质的语法,是给编码者看的
  18. const MESSAGE: &str = "mod1 message without pub";
  19. pub(crate) enum Alphabet {
  20. A = 1,
  21. B = 2,
  22. C = 3,
  23. }
  24. pub mod mod2 {
  25. pub const MESSAGE: &str = "mod2 message with pub";
  26. pub fn say() {
  27. println!("{}", super::MESSAGE);
  28. }
  29. }
  30. }
  31. fn main() {
  32. // println!("{}", mod1::MESSAGE); // error[E0603]: constant `MESSAGE` is private
  33. println!("{}", mod1::mod2::MESSAGE); // mod2 如果不加 pub 也会报错
  34. println!("{}", mod1::Alphabet::A as u8); // 1
  35. mod1::mod2::say(); // mod1 message without pub
  36. }

结构体的可见性

  • 结构体的字段和方法默认私有
  • 加上 pub,可以在定义结构体的模块之外可访问
  • ⚠️:与结构体同一个模块的代码访问结构体中的字段和方法并不要求pub ```rust mod mod1 { pub struct Person {

    1. name: String,
    2. pub nickname: String,

    }

    impl Person {

    1. pub fn new(name: &str, nickname: &str) -> Self {
    2. Person {
    3. name: String::from(name),
    4. nickname: String::from(nickname),
    5. }
    6. }
    7. pub fn say_name(&self) {
    8. println!("{}", self.name);
    9. }

    } }

fn main() { let p = mod1::Person::new(“mike”, “mikeNickname”); p.say_name(); println!(“{}”, p.nickname); // println!(“{}”, p.name); // error[E0616]: field name of struct Person is private }

  1. <a name="OJdry"></a>
  2. #
  3. <a name="cJ4Sr"></a>
  4. # 使用 use 绑定模块成员
  5. - 用于简化方法访问路径
  6. ```rust
  7. use std::fs; // 可以和as一起用:use std::fs as stdfs;
  8. fn main() {
  9. let data = fs::read("src/main.rs").unwrap(); // unwrap 让result中包含错误时,程序直接崩溃
  10. println!("{}", String::from_utf8(data).unwrap());
  11. }

使用 super 和 self 简化模块路径

  • 出了使用完整路径访问模块内部成员,还可以用super与self关键字相对路径对模块进行访问
    • super: 上层模块
    • self:当前模块
  • 当上层、当前、子模块中拥有相同名字成员时,使用super和self可以消除歧义 ```rust fn func() { println!(“func called”); }

pub mod mod1 { pub fn func() { super::func(); } pub mod mod2 { fn func() { println!(“mod1::mod2::func called”) }

  1. pub fn call() {
  2. self::func();
  3. }
  4. }

}

fn main() { mod1::func(); mod1::mod2::call(); }

  1. <a name="Jqgxy"></a>
  2. ## <br />
  3. <a name="khIEH"></a>
  4. # 项目目录层次结构
  5. <a name="W7n4C"></a>
  6. ## 将模块映射到文件

src |- main.rs |- mod1.rs

  1. ```rust
  2. // mod1.rs
  3. pub const MESSAGE: &str = "mod1 message";
  4. // main.rs
  5. mod mod1;
  6. fn main() {
  7. println!("{}", mod1::MESSAGE);
  8. }

将模块映射到文件夹

  1. src
  2. |- main.rs
  3. |- mod1
  4. |- mod1_a.rs
  5. |- mod.rs
  1. // mod1/mod.rs
  2. pub mod mod1_a;
  3. pub const MESSAGE: &str = "mod message";
  4. // mod1/mod1_a.rs
  5. pub const MESSAGE: &str = "mod1_a message";
  6. // main.rs
  7. mod mod1;
  8. fn main() {
  9. println!("{}", mod1::mod1_a::MESSAGE);
  10. println!("{}", mod1::MESSAGE);
  11. }