[rust 基础知识] rust 模块管理中的 mod 和 use

8,267 阅读3分钟

[rust 基础知识] rust 模块管理中的 mod 和 use

一、背景

在学习 rust 模块管理的时候,会有两个关键词,moduse,区分清楚这两个关键词,才正确的使用,而不是一遍又一遍地在编译后修复报错。并且本文只区分 modusecratesuper 等关键词,就不解释了。

下面是自己的个人理解。假定初始化项目的文件结构是:

- Cargo.toml
- src/
    - main.rs

一、 mod 关键词

rust 使用 mod 关键词用来定义模块和引入模块。

1.1 定义模块

mod 关键字为起始,然后指定模块的名字,并且用大括号包围模块的主体。

// src/main.rs
mod add {
    pub mod add_one {
        pub fn add_one (base: u32) -> u32 {
            base + 1
        }
    }
}

fn main() {
    print!("{}", add::add_one::add_one(0));
}

该示例的模块树为:

- crate
    - mod add
        - mod add_one
            - fn add_one
    - fn main

1.2 引入模块

使用 mod 组织的模块树结构,可以利用文件结构的目录树形式进行拆分,每个文件或者文件夹就是一个模块。然后以 mod 关键字为起始,然后指定模块的名字,来引入内容。

可以理解成,mod 引入模块,就是将拆分的内容整合后再次放入 main.rs

1.2.1 拆分方式一:拆分成文件

文件结构为:

- Cargo.toml
- src/
    - add.rs
    - main.rs

文件内容为:

// src/add.rs
pub mod add_one {
    pub fn add_one (base: u32) -> u32 {
        base + 1
    }
}
// src/main.rs
mod add;

fn main() {
    print!("{}", add::add_one::add_one(0));
}

在这个示例中,src/add.rs 就是 mod add

1.2.2 拆分方式二:拆分成文件夹

文件结构为:

- Cargo.toml
- src
    - add/
        - add_one.rs
        - mod.rs
    - main.rs

文件内容为:

// src/add/add_one.rs
pub fn add_one (base: u32) -> u32 {
  base + 1
}
// src/add/mod.rs
pub mod add_one;
// src/main.rs
mod add;

fn main() {
    print!("{}", add::add_one::add_one(0));
}

在这个示例中,src/add/mod.rs 就是 mod addsrc/add/add_one.rs 就是 mod add_one

1.2.3 拆分方式三:拆分成文件加文件夹

文件结构为:

- Cargo.toml
- src
    - add/
        - add_one.rs
    - add.rs
    - main.rs

文件内容为:

// src/add/add_one.rs
pub fn add_one (base: u32) -> u32 {
  base + 1
}
// src/add.rs
pub mod add_one;
// src/main.rs
mod add;

fn main() {
    print!("{}", add::add_one::add_one(0));
}

在这个示例中,src/add.rs 就是 mod addsrc/add/add_one.rs 就是 mod add_one

二、 use 关键词

rust 使用 use 关键词用来调整模块内容调用路径。

这里就要对 moduse 进行区分:use 仅仅是在存在模块的前提下,调整调用路径,而没有引入模块的功能,引入模块使用 mod

先简单看一下,use 的使用:

mod add {
    pub mod add_one {
        pub fn add_one (base: u32) -> u32 {
            base + 1
        }
    }
}

use add::add_one;

fn main() {
    print!("{}", add_one::add_one(0));
}

通过 use 调整之后,不必每次使用都要写出完整的调用路径了。

三、子模块之间相互调用

项目的文件结构是:

- Cargo.toml
- src/
    - add.rs
    - equal.rs
    - main.rs

文件内容为:

// src/add.rs
use super::equal::equal_one;

pub mod add_one {
  pub fn add_one (base: u32) -> u32 {
      base + 1
  }

  pub fn add_one_then_equal_one (base: u32) -> bool {
    super::equal_one::equal_one(add_one(base))
  }
}
// src/equal.rs
pub mod equal_one {
  pub fn equal_one (base: u32) -> bool {
      base == 1
  }
}
mod add;

use add::add_one;

fn main() {
    print!("{}", add_one::add_one_then_equal_one(0));
}

现在这个例子运行是报错的,会提示 use of undeclared crate or module equal。这是因为 add.rs 中通过 use 调整了 equal 模块的引入路径,但是 equal 模块现在还不存在。

按照理解,虽然内容进行了拆分,但是还会整合的,所以整合之后的 main.rs 内容应该理解成是(通过将拆分的内容复原来查找问题):

mod add {
  use super::equal::equal_one;
  mod add_one {
    pub fn add_one;
    pub fn add_one_then_equal_one;
  }
}

use add::add_one;

fn main;

在通过 use super::equal::equal_one 调整路径时,super 将路径指到了顶层,缺少 equal 模块,所以我们使用 mod 关键词引入即可,调整 main.rs 的内容:

mod add;
mod equal;

use add::add_one;

fn main() {
    print!("{}", add_one::add_one_then_equal_one(0));
}

运行成功,可以实现子模块之间相互调用。