Skip to main content

包`<Package>` 单元包`<Crate>` 模块`<Module>`

Rust 的代码组织

  • 代码组织主要包括:
    • 那些细节可以暴露,哪些细节是私有的。
    • 作用域内哪些名称有效。

模块系统

  • Package(包):Cargo 的特性,让你构建、测试、共享 crate,它是最顶层的。
  • Crate(单元包):一个模块树,它可以生产一个 library库 或 可执行文件。
  • Module(模块)、use:让你控制代码的组织、作用域、私有路径。
  • Path(路径):为 structfunctionmodule 等项命名的方式。

Package 和 Crate

包与单元包

  • Crate 的类型

    • binary
    • library
  • Crate Root

    • 是源代码文件
    • Rust 编译器从这里开始,组成你的 Crate 的根 Module
  • 一个 Package

    • 包含 1 个 Cargo.toml,它描述了如何构建这些 Crates
    • 只能包含 0-1 个 library crate
    • 可以包含任意数量的 library crate
    • 但必须至少包含一个 cratelibrarybinary)。

Cargo 的惯例

  • src/main.rs:有这个就说明有二进制的 crate

    • binary cratecrate root cargo 会默认把这个文件作为根
    • crate 名与 package 名相同
  • src/lib.rs:有这个就说明有库 crate

    • package 包含一个 library crate
    • library cratecrate root
    • crate 名与 package 名相同
  • 这两个都是入口文件,都是 crate 的根,并且名字与项目名相同。

  • Cargocrate root 文件交给 rustc 来构建 librarybinary

  • 一个 Package 可以同时包含 src/main.rssrc/lib.rs

  • 一个 binary crate,一个 library crate

  • 名称与 Package 可以有多个 binary crate

    • 文件放在 src/bin
    • 而且每个文件是单独的 binary crate

Cargo 的作用

  • 将相关功能组合到一个作用域内,便于项目间进行共享

    • 防止命名冲突
  • 例如 rand crate,访问它的功能需要通过它的名字:rand

定义 module 来控制作用域和私有性

backyard
├── Cargo.lock
├── Cargo.toml
└── src
├── garden
│   └── vegetables.rs
├── garden.rs
└── main.rs
  • Module

    • 在一个 crate 内,将代码进行分组。
    • 增加可读性,易于复用。
    • 控制项目(item)的私有性。public、private
  • 建立 module

    • mod 关键字
    • 可嵌套
    • 可包含其他项(struct、enum、常量、trait、函数等)的定义
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() {}
}
}
tip
crate
└── front_of_house
├── hosting
│ ├── add_to_waitlist
│ └── seat_at_table
└── serving
├── take_order
├── serve_order
└── take_payment

  • src/main.rs 和 src/lib.rs 叫做 crate roots:
    • 这两个文件(任意一个)的内容形成了名为 crate 的模块,位于整个模块树的根部。
    • 整个模块树都被放在一个名字为 crate 的隐式根模块下面了。

路径(Path)

为了在 Rust 的模块中找到某个条目,需要使用路径。

  • 路径的两种形式:

    • 绝对路径:从 crate root 开始,使用 crate 名 或 字面值 crate
    • 相对路径:从当前模块开始,使用 selfsuper 或当前模块的标识符
  • 路径至少由一个标识符组成,标识符之间使用 ::

私有边界 private boundary

  • 模块不仅可以组织代码,还可以定义私有边界。
  • 如果想把 函数struct 等设为私有,可以将它放到某个模块中。
  • Rust 中所有的条目(函数,方法,struct,enum,模块,常量)默认是私有的。
  • 父级模块无法访问子模块中的私有条目。
  • 子模块里可以使用所有祖先模块中的条目。
默认是私有的
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() {}
}
}

// pub 是定义公共模块
pub fn eat_at_restaurant() {
crate::front_of_house::hosting::add_to_waitlist();
front_of_house::hosting::add_to_waitlist();
}

super

这个东西类似文件系统的 ..

fn serve_order() {}

mod back_of_house {

fn fix_incorrect_order() {
cook_order();
// 相对路径
super::serve_order();
// 绝对路径
crate::serve_order();
}

fn cook_order() {}
}

pub struct

  • pub 放在 struct 前:

    • struct 是公共的
    • struct 的字段默认是私有的
  • struct 的字段需要单独设置 pub 来变成公有的。

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("ACE");
meal.toast = String::from("Wheat");
println!("{}", meal.toast);
meal.seasonal_fruit = String::from("blueberries"); // 这是私有的 所以报错
}

pub enum

💡 注意:enum 设置 pub ,里面所有的属性都是私有的

mod  back_of_house {
pub enum Appetizer {
Soup,
Salad
}
}

use

  • 可以使用 use 关键字将路径导入到作用域内

    • 仍遵循私有性规则
  • 使用 use 来指定相对路径

  • 习惯用法

函数:将函数的父级模块引入作用域(指定到父级)
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
fn some_function() {}
}
}

use crate::front_of_house::hosting; // 绝对路径
use front_of_house::hosting; // 相对路径

pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
hosting::add_to_waitlist();
hosting::add_to_waitlist();
hosting::some_function(); // 没有遵循私有性规则
}

struct,enum,其他:指定完整路径(指定到本身)
use std::collections::HashMap;

fn main() {
let mut map = HashMap::new();
map.insert(1, 2);
}
  • 同名条目:指定到父级
use std::fmt;
use std::io;

fn f1() -> fmt::Result {}

fn f2() -> io::Result {}

fn main() {}

as

use std::fmt::Result as Fmt_Result;
use std::io::Result as IO_Result;

fn f1() -> Fmt_Result {}

fn f2() -> IO_Result {}

fn main() {}

pub use

  • 使用 use 将路径(名称)导入到作用域内后,该名称在此作用域内是私有的。
  • 用来重新导出
    • 既可以将条目引入当前的作用域
    • 该条目又可以被外部代码引入到它们的作用域
🌰

pub use crate::front_of_house::hosting;

使用外部的包(package)

  • Cargo.toml 添加依赖的包(package)

    • Cargo.toml 就会从 https://crates.io 上面来下载这个包和包的依赖项下载到本地。
  • use 将特定条目引入作用域

[dependencies]
rand = "0.5.5"

use rand::Rng
  • 标准库(std)也被当作外部包
    • 不需要修改 Cargo.toml 来包含 std
    • 需要使用 use 将 std 中的特定条目引入当前作用域
标准库
use std::collections::HashMap;

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

  • 如果使用同一个包或模块下的多个条目
use std::io;
use std::cmp::Ordering;

  • 可以使用嵌套路径在同一行内将上述条目进行引入:
    • 路径相同的部分::{路径差异的部分}
use std::{ io, cmp::Ordering };
如果想要引入相同包下面的方法
use std::io::{ self, Write };

通配符 *

  • 使用 * 可以把路径中所有的公共条目都引入到作用域。
use std::collections::*;
谨慎使用
  • 测试。将所有被测试代码引入 tests 模块。
  • 有时被用于预导入(prelude)模块

拆分模块

  • 将模块内容移动到其它文件

    • 模块定义时,如果模块名后边时 “;” , 而不是代码块;
    • Rust 会从与模块同名的文件中加载内容。
    • 模块树的结构不会发生变化。
    lib.rs
    pub use crate::front_of_house::hosting;

    pub mod front_of_house;

    pub fn eat_at_restaurant() {
    hosting::test()
    }
    front_of_house.rs
    pub mod hosting {
    pub fn test() {}
    }
    • 目录的层级结构要跟模块的层级结构相匹配
    lib.rs
    pub use crate::front_of_house::hosting;

    pub mod front_of_house;

    pub fn eat_at_restaurant() {
    hosting::test()
    }
    front_of_house.rs
    pub mod hosting;
    src/front_of_house/hosting.rs
    pub fn test() {}
  • 随着模块逐渐变大,该技术让你可以把模块的内容移动到其它文件中