Rust 模块系统初探

Rust 模块系统初探 在 2018/11/07 16:16:57 发布于 编程实践 分类

学习一门编程语言,知道如何引用外部的文件或者说模块系统是比较重要的一点,PHP用autoload机制,C有经典的include。最近写 wslexe 的时候,因为原作者用的 rust,所以就简单学习了一下。但是 rust 的模块文档是从顶部设计开始写的,很多概念,有些复杂,这里写一个应用导向的学习笔记。

引入文件

首先,让我们初始化一个项目。

mkdir rustmod
cd rustmod
cargo init

我们得到了下面这样的基础项目结构

rustmod
├── Cargo.toml
└── src
    └── main.rs

main.rs里面只有一个简单的输出的‘hello world’的main函数。我们新增一个functions.rs文件,将生成字符串的过程做成一个函数,供main函数调用。

//file: rustmod/src/functions.rs
pub fn hello(name: String) -> String {
    format!("Hello, {}!", name)
}

functions.rs就成为了一个单文件模块,rust 中称为mod,模块的名称就是文件的名称。模块内部的函数,只能在模块内部使用,如果要在模块外调用,需要用pub关键词,显式声明函数可在外部使用,我自己把这个叫导出函数。

下面我尝试在main函数中使用functions模块。使用的方式很简单,只要在使用前声明一下模块就可以了。声明模块使用mod关键字。使用模块内函数的时候,需要用::进行调用。

mod functions;//声明模块
fn main() {
    let s = functions::hello("World".to_string());
    println!("{}", s)
}

如果去掉函数前的pub关键词,编译器就会报错。

error[E0603]: function `hello` is private
模块的嵌套

rust 除了一个文件作为一个模块之外,还持支模块的嵌套。嵌套的方式,有两种。

第一种是在一个文件内,用mod关键字定义子模块。比如,我修改functions.rs

//file: rustmod/src/functions.rs
pub mod util {
    pub fn hello(name: String) -> String {
        format!("Hello, {}!", name)
    }
}

同样,模块也是私有的,要在外部调用也需要pub关键字修饰。对应的main函数就要改为

//file: rustmod/src/main.rs
mod functions;
fn main() {
    let s = functions::util::hello("World".to_string());
    println!("{}", s)
}

第二种形式,是用文件夹来组织模块。文件夹的名字是模块的名字,rust 会去寻找文件夹下面的mod.rs作为模块的主文件,可以在里面写任意的 rust 代码,不过大多数时候用来输出目录内的子模块。我们来新建一个util文件夹,作为一个工具库的模块,修改项目结构

rustmod
├── Cargo.toml
└── src
    ├── main.rs
    └── util
        ├── functions.rs
        └── mod.rs

mod.rs输出functions模块。同样也需要用pub关键词修饰,否则就只是mod.rs文件内的私有子模块。

//file: rustmod/src/util/mod.rs
pub mod functions;

修改main.rs中的调用

//file: rustmod/src/main.rs
mod util;
fn main() {
    let s = util::functions::util::hello("World".to_string());
    println!("{}", s)
}
总结

rust 的模块由mod关键字和文件系统两者结合定义。mod关键字用来声明一个模块的名字,模块的内容或直接以mod NAME {MOD CONTENT}的形式书写,或根据严格的规则查找对应源文件。文件查找规则有以下几点:

  1. 查询声明模块的文件所在目录中对应模块名称名字的NAME.rs文件,如果有就作为模块使用。
  2. 查询声明模块的文件所在目录中对应模块名字NAME的文件夹,并且文件夹内有mod.rs文件,如果有就以mod.rs文件作为模块使用。
  3. 上面的规则都可以互相嵌套。

我尝试了在src目录下,同时存在util.rsutil文件夹,编译的时候报错

error[E0584]: file for module `util` found at both util.rs and util/mod.rs

所以还有第四条规则:

  1. 在同一层级不能同时存在文件夹和文件类型的模块,否则会名字冲突。