模块化 & 包管理

包管理(Cargo)

cargo 命令创建包

cargo new xxx --lib 创建一个名为xxx的包;

cargo new xxx 或者 cargo new xxx --bin 创建一个名为xxx的可被编译为可执行文件的包。

使用第三方包

  1. 在Cargo.toml中的[dependencies]下加入包的依赖;
  2. 在需要引入的文件头部加入 extern crate 包名; 之后才可以use 包(Rust 2015)。在2018中,直接可以用use xxx。

Note:Cargo默认把连字符替换为下划线。

Cargo文件格式

[package]表配置

描述的都是与包相关的元数据,比如包名,作者等。数组使用[],多段文字使用”””。

build = "buidl.rs" 指定构建脚本; workspace = ".." 指定工作空间为父目录。

[badges]表配置

云端的持续集成服务。

[workspace]表配置

members = ["bench", "regex-capi"] 指定子包。

[dependencies]表配置

配置依赖文件。

[features]表配置

条件编译功能相关。在代码中对应#[cfg(featrue = "xxx")]

[lib]表配置

表示最终编译目标库的信息:name,crate-type, path, test, bench等。

[ [test] ]表配置

用两个中括号,表示数组。

[profile]表配置

自定义rustc编译配置。

模块系统

如果存在与文件名同名的目录,则该目录下的模块都是该文件的子模块。

src目录结构

read_func.rs

1
2
3
4
5
pub mod static_kv;  //pub关键字使得可以在main.rs中:use read_func::static_kv
pub fn read_kv () {
}
pub fn rw_mut_kv() -> Result<(), String> {
}

Rust会通过mod关键字去当前模块的子模块中寻找static_kv模块。

read_func/static_kv.rs

1
2
3
use lazy_static::lazy_static;
use std::collections::HashMap;
use std::sync::RwLock;

main.rs

1
2
3
4
mod read_func;
use crate::read_func::{read_kv , rw_mut_kv}; //因为之前声明了pub
fn main() {
}

第一行mod引入模块。

第二行的crate可以用self代替,代表当前的crate,以main.rs为起点寻找当前相对路径下的read_func模块。如果是第三方包,就不需要写crate前缀。

模块间的关系

模块嵌套

1
2
3
4
5
6
7
8
9
10
11
12
13
mod sound {
mod instrument {
mod woodwind {
fn clarinet() {
// 函数体
}
}
}
mod voice {
}
}
fn main() {
}

树形结构:

1
2
3
4
5
crate
└── sound
├── instrument
│ └── woodwind
└── voice

私有性规则有如下:

  • 所有项(函数、方法、结构体、枚举、模块和常量)默认是私有的。
  • 可以使用 pub 关键字使项变为公有。
  • 不允许使用定义于当前模块的子模块中的私有代码。
  • 允许使用任何定义于父模块或当前模块中的代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
mod sound {
pub mod instrument { //加上Pub后可用路径访问instrument
pub fn clarinet() { //加上pub后可以用该函数
// 函数体
}
}
}
fn main() {
// 绝对路径
crate::sound::instrument::clarinet();
// 相对路径
sound::instrument::clarinet();
}

也可以使用 super 开头来构建相对路径。这么做类似于文件系统中以 .. 开头:该路径从 模块开始而不是当前模块。

1
2
3
4
5
6
7
8
mod instrument {
fn clarinet() {
super::breathe_in();
}
}
fn breathe_in() {
// 函数体
}

clarinet 函数位于 instrument 模块中,所以可以使用 super 进入 instrument 的父模块,也就是根 crate。从这里可以找到 breathe_in。使用super相对路径可以方便的进行扩展,而不用更改路径来调用。

sound模块放入sound.rs文件中,调用方式不变。

重新导出

1
pub use crate::sound::instrument;

可以简化外部调用的导出路径(外部调用:use xxx::instrument; ),也不需要对外暴露模块(sound)。

一般重新导出放在lib.rs中。main.rs结合lib.rs的形式,是二进制包的最佳实践。

可见性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
pub mod outer_mod {
pub(self) fn outer_mod_fn() {}

pub mod inner_mod {
// 在Rust 2018 edtion 模块系统必须使用use导入
use crate::outer_mod::outer_mod_fn;
// 对外层模块 `outer_mod` 可见
pub(in crate::outer_mod) fn outer_mod_visible_fn() {}
// 对整个crate可见
pub(crate) fn crate_visible_fn() {}
// `outer_mod` 内部可见
pub(super) fn super_mod_visible_fn() {
// 访问同一模块的函数
inner_mod_visible_fn();
// 使用use导入了outer_mod
outer_mod_fn();
}
// 仅在`inner_mod`可见
pub(self) fn inner_mod_visible_fn() {}
}

pub fn foo() {
inner_mod::outer_mod_visible_fn();
inner_mod::crate_visible_fn();
inner_mod::super_mod_visible_fn();

// 不能使用inner_mod 的私有函数
// inner_mod::inner_mod_visible_fn();
}
}
fn bar() {
// 该函数对整个crate可见
outer_mod::inner_mod::crate_visible_fn();

// 该函数只对outer_mod可见
// outer_mod::inner_mod::super_mod_visible_fn();

// 该函数只对outer_mod可见
// outer_mod::inner_mod::outer_mod_visible_fn();

// 通过foo函数调用内部细节
outer_mod::foo();
}
fn main() { bar() }

关于pub:

  • 如果不显示使用pub,则函数或者模块可见性默认为私有的;
  • pub,可以对外暴露公共接口;
  • pub(crate),对整个crate可见;
  • pub(in Path),其中Path是模块路径,表示可以通过此Path路径来限定可见范围;
  • pub(self) / pub(in self),只限当前模块可见;
  • pub(super) / pub(in super),当前模块和父模块中可见。

Note:trait中关联类型和Enum中变体的可见性,会随着trait和Enum的可见性而变化。但是结构体中的字段需要单独使用pub来改变可见性。

本文标题:模块化 & 包管理

文章作者:木南

发布时间:2019年09月04日 - 17:09

最后更新:2019年09月05日 - 16:09

原始链接:http://munan.tech/2019/09/04/模块化/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

点击下方打赏按钮,获得支付宝二维码