Rust: Managing Growing Projects with Packages, Crates, and Modules

Managing Growing Projects with Packages, Crates, and Modules

Packages and Crates

Package

Package(包) : Cargo 的特性,让你构建、测试、共享 crate

Crate(单元包) : 模块树,它可产生一个 library 或可执行文件

Module(模块), use : 让你控制代码的组织、作用域、私有路径

Path(路径) : 为 struct、function 或 module 等项命名的方式

Cargo 的惯例

src/main.rs:
	- binary crate 的 crate root
	- crate 名与 package 名相同

src/lib.rs:
	- package 包含一个 library crate
	- library crate 的 crate root
	- crate 名与 package 名相同
一个 package 可以同时包含 src/main.rs 和 src/lib.rs
	- 名称与 package 名相同

一个 package 可以有多个 binary crate:
	- 文件放在 src/bin
	- 每个文件是单独的 binary crate

一个 package 最多有一个个 library crate:

Defining Modules to Control Scope and Privacy

module

Module:
	- 在一个 crate 内,将代码进行分组
	- 增加可读性,易于复用
	- 控制项目(item)的私有性。public、private
建立 module:
	- 使用 mod 关键字
	- 可以嵌套

例如

mod front_of_house {
    mod hosting {
        fn add_to_waiting() {}
    }
}

pub 关键字

pub struct:
	- struct 是公共的
	- struct 的字段默认私有

例如

mod front_of_house {
    pub struct Breakfast {
        pub toast: String,// public
        seasonal_fruit: String,// private
    }

    impl Breakfast {
        pub fn summer(toast: &str) -> Breakfast {
            Breakfast {
                toast: String::from(toast),
                seasonal_fruit: String::from("peaches"),
            }
        }
    }
}

fn eat_at_restaurant() {
    let mut meal = front_of_house::Breakfast::summer("Rye");
    meal.toast = String::from("Wheat");
    meal.seasonal_fruit = String::from("blue");// Error! private field
}

Paths for Referring to an Item in the Module Tree

Path

绝对路径:从 crate root 开始,使用 crate 名 或 字面值 crate

相对路径:从 当前模块开始,使用 self,super 或当前模块的标识符

mod front_of_house {
    mod hosting {
        fn add_to_waiting() {}
    }
}

fn eat_at_restaurant() {
	// 绝对路径
    crate::front_of_house::hosting::add_to_waiting();
	//相对路径
    front_of_house::hosting::add_to_waiting();
}

Cargo run 会发现错误

/*
house::hosting::add_to_waiting();
   |           ^^^^^^^  -------------- function `add_to_waiting` is not publicly re-exported
   |           |
   |           private module
*/

这就是因为默认私有,使用 pub 关键字将条目标记为公共的

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waiting() {}
    }
}

父级模块无法访问子模块中的私有条目

子模块可以访问所有祖先模块中的条目

super 关键字,表示上一级

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waiting() {
            super::get();// 调用上一级的 get            
        }
    }

    fn get() -> String {
        return String::from("2024");
    }
}

Bringing Paths into Scope with the use Keyword

遵守私有性原则

一般将函数的的父级引入模块

struct,enum,其他:指定完整路径(指定到本身)

mod front_of_house {
    pub struct Breakfast {
        pub toast: String,
        seasonal_fruit: String,
    }

    impl Breakfast {
        pub fn summer(toast: &str) -> Breakfast {
            Breakfast {
                toast: String::from(toast),
                seasonal_fruit: String::from("peaches"),
            }
        }
    }
}

use crate::front_of_house::Breakfast;// 绝对路径

fn eat_at_restaurant() {
    Breakfast::summer("abc");//这时候就可直接用 
}

同名条目:指定到父级

use std::fmt;
use std::io;

fmt::Result;
io::Result;

as 关键字

use std::fmt::Result as FmtResult;
use std::io::Result as IoResult;

这时候就可以直接用 as 后的东西代替前面的东西

use 引入默认私有,外部代码看不到

fn eat_at_restaurant() {
    use crate::front_of_house::Breakfast; // 外部看不到
    let x = Breakfast::summer("abc");
    println!("{:#?}", x);
}
fn eat_at_restaurant() {
    pub use crate::front_of_house::Breakfast;// 外部看得到
    let x = Breakfast::summer("abc");
    println!("{:#?}", x);
}

使用外部包 package

Cargo.toml 下添加 package

package 下载 https://crates.io/

// Cargo.toml 下
[dependencies]
rand = "0.7.3"
// package

需要 cargo build, 下载慢可以换国内镜像源

使用 use 将特定条目引入作用域

use rand::Rng;

std 默认内置(即不需要在 dependencies 放 package),但需要用 use 来引入作用域

例如引入 HashMap

use std::collections::HashMap;

使用嵌套路径清理大量 use 语句

use std::collections::HashMap;
use std::collections::HashSet;
use std::collections::BTreeMap;

我们发现前面都是一样的

使用语法 相同部分::{不同部分,...}

use std::collections::{HashMap, HashSet, BTreeMap};

当我们需要本身和它下面的条目时,使用 self

例如:引入 std::collections 和 std::collections::HashMap

use std::collections::{self, HashMap};// self

当我们需要一个模块下所有条目时,我们可以使用通配符 * 来引入

use std::collections::*;

一般测试时才会用

Separating Modules into Different Files

将模块内容移动到其他文件

模块定义时,如果模块名后面是;,而不是代码块{code},rust 会从与模块同名的文件中加载内容,模块树结构不会变化

// lib.rs 内
mod hosting;

// hosting.rs 内
mod hosting {
	// code
}

如果是嵌套模块,需要放到对应文件夹下