你的位置:首页 > 信息动态 > 新闻中心
信息动态
联系我们

把Rust 模块系统讲地明明白白的

2021/11/22 23:48:29

文章目录

  • 例1
  • 例2
  • 例3
  • super
  • use
  • 扩展模块
  • 总结

Rust 模块系统非常让人困惑,并且使新手有非常大的失败感.

在本文中,我会用实际例子来解释模块系统,你会清楚明白它是怎样工作的,并且可以立即应用到你们的项目中.

由于Rust模块系统十分独特,我请大家读本文时需要打开思维,不要往其它语言上套.

用以下结构来模拟真实项目:

my_project
├── Cargo.toml
└─┬ src
  ├── main.rs
  ├── config.rs
  ├─┬ routes
  │ ├── health_route.rs
  │ └── user_route.rs
  └─┬ models
    └── user_model.rs

有不同的方式来让我们使用module
在这里插入图片描述
These 3 examples should be sufficient to explain how Rust’s module system works.

3个例子应该足以解决Rust模块系统是怎样工作的

例1

// main.rs
fn main() {
  println!("main");
}
// config.rs
fn print_config() {
  println!("config");
}

第一个错误是,由于有些文件 config.rs, health_route.rs等,我们认为这些文件是modules,我们可以导入他们到其它文件中.

下边是我位看到的文件系统树和编译器看到的模块树
在这里插入图片描述
非常意外,编译器只能看到crate模块,也以是main.rs文件,这是因为我们需要在Rust中显示构建模块树 - 文件系统树和模块树并没有隐式映射.

我们需要显示构建Rust模块树,没有隐式映射到文件系统功能

为了把文件添加到模块树中,需要声明使用mod关键字声明文件为子模块.下面的还可能使人困惑,你应该假设我们声明文件为一个模块,在同一个文件中.但是我们声明它在不同文件.由于我们只有main.rs在模块树中,让我们声明config.rs作为main.rs的子模块.

mod 关键字声明子模块

关键字mod的语法是
mod my_module

下边的编译器会在相同的目录中寻找my_module.rs或my_module/mod.rs

my_project
├── Cargo.toml
└─┬ src
  ├── main.rs
  └── my_module.rs

或

my_project
├── Cargo.toml
└─┬ src
  ├── main.rs
  └─┬ my_module
    └── mod.rs

因为main.rs和config.rs是在相同的目录,让我们声明config为一个模块吧

// main.rs
+ mod config;

fn main() {
+ config::print_config();
  println!("main");
}
// config.rs
fn print_config() {
  println!("config");
}

我们访问函数print_config用::语法

现在模块树是这样了
在这里插入图片描述
我们成功的声明了config模块!但是还不能调用config.rs中print_config(). 几乎在Rust中所有东西默认都是私有的,我们需要用关键字pub使函数为公有.

关键字pub可以使访问变为公有.

// main.rs
mod config;

fn main() {
  config::print_config();
  println!("main");
}
// config.rs
- fn print_config() {
+ pub fn print_config() {
  println!("config");
}

现在,成功了,我位成功的在不同文件中调用定义的函数了.

例2

我们来从main.rs中调用routes/health_route.rs中的print_health_route().

// main.rs
mod config;

fn main() {
  config::print_config();
  println!("main");
}
// routes/health_route.rs
fn print_health_route() {
  println!("health_route");
}

就像前边所说的,使用关键字mod仅仅是my_module.rs 或 my_module/mod.rs在同个目录才行.

所以为了从main.rs中调用routes/health_route.rs里边的函数,我们需要做以下事:

  • 创建名为routes/mod.rs文件,并且在main.rs中定义routes子模块.
  • routes/mod.rs中定义子模块health_route,并且使它的访问权限为公开的.
  • 使health_route.rs中访问权限为公开的.
my_project
├── Cargo.toml
└─┬ src
  ├── main.rs
  ├── config.rs
  ├─┬ routes
+ │ ├── mod.rs
  │ ├── health_route.rs
  │ └── user_route.rs
  └─┬ models
    └── user_model.rs
// main.rs
mod config;
+ mod routes;

fn main() {
+ routes::health_route::print_health_route();
  config::print_config();
  println!("main");
}
// routes/mod.rs
+ pub mod health_route;// routes/mod.rs
+ pub mod health_route;
// routes/health_route.rs
- fn print_health_route() {
+ pub fn print_health_route() {
  println!("health_route");
}

下边是模块树的样子
在这里插入图片描述
现在可以调用文件夹内文件中的函数了.

例3

让我们尝试调用从main.rs => routes/user_route.rs => models/user_model.rs

// main.rs
mod config;
mod routes;

fn main() {
  routes::health_route::print_health_route();
  config::print_config();
  println!("main");
}

// routes/user_route.rs
fn print_user_route() {
  println!("user_route");
}
// models/user_model.rs
fn print_user_model() {
  println!("user_model");
}

我们想要从main中调用print_user_route 再调用print_user_model中函数.

我们把之前的再做一遍 - 定义子模块,修改函数访问权限为共有,再添加mod.rs文件

my_project
├── Cargo.toml
└─┬ src
  ├── main.rs
  ├── config.rs
  ├─┬ routes
  │ ├── mod.rs
  │ ├── health_route.rs
  │ └── user_route.rs
  └─┬ models
+   ├── mod.rs
    └── user_model.rs
// main.rs
mod config;
mod routes;
+ mod models;

fn main() {
  routes::health_route::print_health_route();
+ routes::user_route::print_user_route();
  config::print_config();
  println!("main");
}
// routes/mod.rs
pub mod health_route;
+ pub mod user_route;
// models/mod.rs
+ pub mod user_model;
// models/user_model.rs
- fn print_user_model() {
+ pub fn print_user_model() {
  println!("user_model");
}

模块树现在长成这样
在这里插入图片描述
等等,我们实际还不能在print_user_route中调用print_user_model!到目前为止,我们只是从main.rs中调用其它模块中定义好的函数,在其它文件中我们该怎么做?

如果我位看看模块树, 函数print_user_model 在crate::models::user_model路径下.所以为了使用在文件中的模块,不是说main.rs.我们应该想想依据必要的模块树中的模块路径来访问.

// routes/user_route.rs
pub fn print_user_route() {
+ crate::models::user_model::print_user_model();
  println!("user_route");
}

我们成功在非main.rs文件中调用到其它文件中定义的函数了.

super

如果我们组织文件有多层目录,引用完整的名字太长了. 假如某个原因,我们想从print_user_route中调用print_health_route.会分别有下边两个路径crate::routes::health_routecrate::routes::user_route.

我们调用它通过使用完全的引用名crate::routes::health_route::print_health_route(),但是我们也可以使用相对路径super::health_route::print_health_route().注意用super来指向父作用域.

模块路径的关键字super指定父作用范围.

pub fn print_user_route() {
  crate::routes::health_route::print_health_route();
  // can also be called using
  super::health_route::print_health_route();

  println!("user_route");
}

use

在上边例子中使用完整引用名甚至相对引用名是冗长的.为了使引用名更短些,我们使用关键字use来绑定新模块名字或重命名.

关键字use被用来缩短模块路径

pub fn print_user_route() {
  crate::models::user_model::print_user_model();
  println!("user_route");
}

上边代码可以重构为

use crate::models::user_model::print_user_model;

pub fn print_user_route() {
  print_user_model();
  println!("user_route");
}

替换使用print_user_model名字,可以重命名.

use crate::models::user_model::print_user_model as log_user_model;

pub fn print_user_route() {
  log_user_model();
  println!("user_route");
}

扩展模块

添加依赖到Cargo.toml,在项目中所有模块都是可用的.不需要显示引入或声明任何其它的东西,以可以使用依赖.

在整个项目中的模块,扩展依赖是全局可用的
例如,在项目中添加rand crate, 我们可以在代码中直接使用

pub fn print_health_route() {
  let random_number: u8 = rand::random();
  println!("{}", random_number);
  println!("health_route");
}

也可以使用use来缩短引用路径

use rand::random;

pub fn print_health_route() {
  let random_number: u8 = random();
  println!("{}", random_number);
  println!("health_route");
}

总结

  • 模块系统是显示引用的 - 并没有关系一对一和文件系统映射
  • 声明文件作为模块在他父亲中,不是在他自己里边
  • 关键字mod被用来声明子模块
  • 需要显示声明函数,结构等访问权限为公用的,所有他们可以在其它模块里.
  • 关键字pub使代码访问权限为共有
  • 不需要显示声明第三方模块