跳到主要内容

Hello Rust [4] - 模块

包和Crate

crate 是一个二进制项或者库, crate root是一个源文件。
Rust编译器以它为起始点,并构成你的crate的根模块。
包中所包含的内容由几条规则来确立。

  • 包中可以包含最多一个库crate
  • 包中可以包含任意多个二进制crate
  • 至少包含一个crate,二进制或者库

模块

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

fn seat_at_table() {}
}

mod serving {
fn take_order() {}

fn serve_order() {}

fn take_payment() {}
}
}
  • 定义一个模块,关键字为mod,语法如上。
  • 在模块内部,可以定义其他的项:结构体,枚举,常量,特性,函数
  • src/main.rssrc/lib.rs叫做crate
  • 这两个文件都分别在crate模块的根组成一个叫crate的模块,该结构被称为 模块树 (module tree)
crate
└── front_of_house
├── hosting
│ ├── add_to_waitlist
│ └── seat_at_table
└── serving
├── take_order
├── serve_order
└── take_payment

模块路径

有两种路径:

  • 绝对路径(absolute path):从crate根开始,以crate名或者字面值crate开头。
  • 相对路径(relative path):从当前模块开始,以selfsuper或当前模块的表示开头。

绝对路径和相对路径都后面更一个或者多个由双冒号(::)分割的标识符。
下面是一个例子:

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

pub fn eat_at_restaurant() {
// 绝对路径
crate::front_of_house::hosting::add_to_waitlist();

// 相对路径
front_of_house::hosting::add_to_waitlist();
}
  • rust中,默认所有项都是私有的。父模块不能使用子模块中的私有项,子模块可以使用父模块的项。
  • pub 可以用于暴露路径。
mod front_of_house {
pub mod hosting {
// 这里,每个方法都要是pub的才能访问
pub fn add_to_waitlist() {}
}
}

pub fn eat_at_restaurant() {
// 绝对路径
crate::front_of_house::hosting::add_to_waitlist();

// 相对路径
front_of_house::hosting::add_to_waitlist();
}

super

super开始的路径类似于文件系统中的../

fn serve_order() {}

mod back_of_house {
fn fix_incorrect_order() {
cook_order();
super::serve_order();
}

fn cook_order() {}
}

公有结构体和枚举

mod back_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"),
}
}
}
}

pub fn eat_at_restaurant() {
// 在夏天订购一个黑麦土司作为早餐
let mut meal = back_of_house::Breakfast::summer("Rye");
// 改变主意更换想要面包的类型
meal.toast = String::from("Wheat");
println!("I'd like {} toast please", meal.toast);

// 如果取消下一行的注释代码不能编译;
// 不允许查看或修改早餐附带的季节水果
meal.seasonal_fruit = String::from("blueberries");
}
  • 对于结构体,每个字段都默认为私有,你需要手动声明字段是否公有
  • 对于枚举,当枚举为公有,则其所有成员都为公有

USE -- 简化路径调用

为了避免每次都写非常冗长的路径调用,我们可以使用use,类似于import

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

use crate::front_of_house::hosting;
// 或者是使用相对路径的导入
use self::front_of_house::hosting;

pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
hosting::add_to_waitlist();
hosting::add_to_waitlist();
}
  • 指定到hosting而不是add_to_waiting的好处在于区分模块

可以使用as重命名,类似于:

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

使用pub use重新导出:

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

pub use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
hosting::add_to_waitlist();
hosting::add_to_waitlist();
}

使用嵌套路径来消除use

use std::cmp::Ordering;
use std::io;

// ==>
use std::{cmp::Ordering, io};

使用glob运算符批量导入:

use std::collections::*;

Cargo.toml 外部包

rand = "0.8.3"

在仓库中:

use rand::Rng;

// ...

在根中导入所有模块

将多个模块分散到不同的文件中时,我们只需要在根文件中导入所有mod就可以了:

mod front_of_house;

pub use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
hosting::add_to_waitlist();
hosting::add_to_waitlist();
}