包管理

包管理(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编译配置。

模块系统

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

层次实例

read_func.rs

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模块。模块名字可以被以下表述:1.模块名.rs;2.与模块名相同的文件夹,并且该文件夹包含mod.rs

read_func/static_kv.rs

use lazy_static::lazy_static;
use std::collections::HashMap;
use std::sync::RwLock;

main.rs

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

第一行mod引入模块。

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

模块间的关系

模块嵌套

mod sound {
    mod instrument {
        mod woodwind {
            fn clarinet() {
                // 函数体
            }
        }
    }
    mod voice {
    }
}
fn main() {
}

树形结构:

crate
└── sound
    ├── instrument
    │   └── woodwind
    └── voice

私有性规则有如下:

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

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

mod instrument {
    fn clarinet() {
        super::breathe_in();
    }
}
fn breathe_in() {
    // 函数体
}

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

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

重新导出

pub use crate::sound::instrument;

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

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

可见性

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来改变可见性。