基本概念

module

模块 Module 用于将函数或结构体按照功能分组。我们通常把相似的函数或者实现相同功能的或者共同实现一个功能的函数和结构体划分到一个模块中。

Rust中模块的概念类似其他语言中模块或者包的概念,以我们常用的Java为例,Rust模块等价于Java中的一个Package.

crate

在Java中我们可以将Package组织为为Module,在Rust中同样如此:我们可以将Rust中的模块进一步组织为crate.

crate中文翻译为箱|板条,个人感觉crate不够直观,甚至不如直接沿用其他语言的概念.

crate是Rust语言的基本编译单元.在Rust中,一个可执行的二进制文件或者一个库就是一个crate.其中可执行二进制文件和库的唯一区别就是二进制文件中含有main()入口.

cargo

Rust采用cargo作为crate管理程序,简单来说cargo就是包管理器.比如Dart中使用pub来管理包.此外,Rust使用crates.io作为管理所有第三方包的中央存储服务器,这类似Dart中的pub.dev,android中的jcenter.

rust dart
image-20200802201818781

我们可以使用cargo install来从crates.io中安装需要的crate.

总结

上述提到的几个术语,在其他语言中都有对应的概念,下表简单对比下:

Rust术语 说明
crate Rust中编译的基本单元,等价于其他语言中的library中的概念,比如dart中的library
module 模块,等价于其他语言中package的概念,比如Java/dart中的package

module

模块定义

Rust中使用mod关键字来定义一个模块,根据访问权限,模块分为公开模块和私有模块,模块定义格式如下:

mod module_name {
	fn function_name() {
    // code body
  } 
  
	...
}

这里的module_name用于表示模块名,必须是一个合法的标志符,格式和函数名定义一样,如下所示:

mod desktop {

    fn doctor() {
       // code body
    }

} /* desktop */

公开模块&私有模块

Rust中模块和模块中函数默认都是私有权限,外部无法访问.如果需要公开模块或者函数给外部使用,需要使用pub关键字,格式如下:

// 公开模块
pub mod module_name {
  // 公开函数
  pub fn function_name() {
    
  }
  
  // 私有函数
  fn function_name(){
    
  }
}

需要注意,私有模块中的函数必须都是私有的,公开模块中的函数可以是私有的,也可以是公有的.

嵌套module

Rust支持在一个模块中嵌套另一个模块,格式如下:

pub mod first_module {
  pub mod second_module {
    pub mod third_module {
      pub fn say_hello(){
          // code body
      }
    }
  }
}

上述代码中,first_module嵌套了second_module,而second_module又嵌套third_module.在third_module中定义了say_hello().

嵌套模块调用非常简单,只需要用::逐级限定模块名即可,比如first_module::second_module::third_module::say_hello()

范例: 模块定义与使用

这里我们定义一个desktop模块,并提供一个用于进行检测的doctor方法:

// 定义
pub mod desktop {

    fn doctor() {
        println!("Checking...");
    }

} /* desktop */

// 调用
fn main() {
    // 全限定路径
    desktop::doctor();

}

导入模块

和C++类似,Rust使用use关键字来引入需要用到的外部模块中的函数和结构体,通过使用use关键字,我们可以预先引入外部模块的函数和构造体而无需再使用全限定模块.

pub mod desktop {
    pub fn doctor() {
        println!("Checking...");
    }
}

use desktop::doctor;
fn main() {
    // 无需全路径限定
    doctor();
}

crate

现在结合module来演示crate的使用.这里我们准备创建一个名为ugo的crate,其中有commander和desktop两个module.

ugo
	- commander
	- module

创建crate

我们使用cargo来管理我们的rust工程,同样这里也用它来管理crate.首先我们来创建一个名为ugo的工作目录,接下来使用cargo new module-name --lib来创建名为module-name的模块:

cargo new commander --lib
cargo new module --lib

现在我们得到了如下结构的目录:

ugo
├── commander
│   ├── Cargo.toml
│   └── src
│       ├── doctor.rs
│       └── lib.rs
└── desktop
    ├── Cargo.toml
    └── src
        └── lib.rs

Cargo.toml

cargo通过使用Cargo.toml来保存/设置库的一些基本数据,比如版本号,库名称,作者以及三方依赖等.其中toml是一种语义化的配置文件格式:

[package]
name = "desktop"  											# 库名
version = "0.1.0" 											# 版本号
authors = ["author <author@gmail.com>"] # 作者
edition = "2018"													

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]													# 依赖

lib.rs

每个crate中的lib.rs用于指定库公开了哪些可用的模块.使用cargo创建的crate中的lib.rs中默认会有一些代码,这里我们先直接删除.

首先我们commander中添加一个doctor.rs,用于输出当前的环境信息:

pub fn shell_vesion() {
    println!("Shell vesion");
}

接下来,我们在lib.rs中将doctor模块导出:

pub mod doctor;

此时,commander结构如下:

commander
├── Cargo.toml
└── src
    ├── doctor.rs
    └── lib.rs

同样的,我们为desktop也提供类似的实现,

// src/doctor.rs
pub fn desktop_version() {
        println!("Desktop platform");
}

// src/lib.rs
pub mod doctor;

现在我们可以通过cargo build来执行构建操作,同时它也可以用来检查我们crate是否有错误.进入commander/deskop下分别执行下,构建无误的情况下回输出下述信息:

$ cargo build
   Compiling desktop v0.1.0 (/Users/lionoggo/Workspace/Rust/module_test/ugo/desktop)
    Finished dev [unoptimized + debuginfo] target(s) in 0.12s

测试

现在我们来创建一个测试程序,在ugo目录下执行cargo new main --bin即可.ugo-main将使用我们上述的两个crate,并分别调用其函数,先来看下当前工程目录:

ugo
├── commander
│   ├── Cargo.lock
│   ├── Cargo.toml
│   └── src
│       ├── doctor.rs
│       └── lib.rs
├── desktop
│   ├── Cargo.lock
│   ├── Cargo.toml
│   └── src
│       ├── doctor.rs
│       └── lib.rs
└── main
    ├── Cargo.toml
    └── src
        └── main.rs

解析来,打开main中额Cargo.toml,将commander/desktop两个crate配置为本地依赖:

[package]
name = "ugo-main"
version = "0.1.0"
authors = ["author <author@gmail.com>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
# 本地依赖
commander = { path = "../commander" }
desktop = { path = "../desktop" }

使用crate

现在我们要在ugo-main中来使用上述两个crate.要想使用crate中的module,首先需要使用extern关键字引入外部库:

extern crate desktop;
extern crate commander;

接下来分别调用两个crate中的函数:

extern crate desktop;
extern crate commander;

fn main() {
    commander::doctor::shell_version();
    desktop::doctor::desktop_version();
}

在上述代码中,我们使用全限定路径来调用函数,如果crate中的函数比较多,这样用起来五一很麻烦,此时可以使用use导入module:

extern crate desktop;
extern crate commander;

use desktop::doctor::desktop_version;
use commander::doctor::shell_version;

fn main() {
    shell_version();
    desktop_version();
}

执行cargo run,不出意外,结果如下:

$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.00s
     Running `target/debug/ugo-main`
Shell vesion
Desktop platform

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!

Vim高级使用技巧 下一篇