定义模块以控制范围和隐私

在本节中,我们将讨论模块和模块系统的其他部分, 即 paths,它允许您命名项目;带来 path into scope;和关键字将项目设为公共。我们还将讨论 关键字、external packages 和 glob 运算符。usepubas

模块作弊表

在我们了解模块和路径的详细信息之前,我们在这里提供了一个快速的 有关 modules、paths、关键字和关键字工作原理的参考 以及大多数开发人员如何组织他们的代码。我们要走 通过本章中这些规则中的每一个示例,但这是一个 这是提醒模块工作原理的好地方。usepub

  • 从 crate 根开始:编译 crate 时,编译器首先 在 crate 根文件(通常是 src/lib.rs 表示库 crate,src/main.rs 表示二进制 crate)中查找要编译的代码。
  • 声明模块:在 crate 根文件中,你可以声明新的模块; 假设您使用 .编译器将查找 对于这些位置的模块代码:mod garden;
    • 内联,在替换分号后面的大括号内mod garden
    • 在文件 src/garden.rs
    • 在文件 src/garden/mod.rs
  • 声明子模块:在 crate 根目录以外的任何文件中,你可以 declare 子模块。例如,您可以在 src/garden.rs 中声明。编译器将在 目录中:mod vegetables;
    • 内联,紧跟在 , 大括号内 分号mod vegetables
    • 在文件 src/garden/vegetables.rs
    • 在文件 src/garden/vegetables/mod.rs
  • 模块中的代码路径:一旦模块成为 crate 的一部分,您就可以 从同一 crate 中的其他任何位置引用该模块中的代码,只要 在隐私规则允许的情况下,使用 code 的路径。例如,garden vegetables 模块中的类型可以在 中找到。Asparaguscrate::garden::vegetables::Asparagus
  • 私有与公共:模块中的代码与其父级相比是私有的 modules 的 Module。要将模块设为公共模块,请使用 instead 而不是 .要将公共模块中的项也设为公共的,请在其声明之前使用 。pub modmodpub
  • use 关键字:在范围内,关键字创建快捷方式 项以减少长路径的重复。在任何可以引用的范围内,您都可以使用 创建快捷方式,从那时起,您只需要 write 以在作用域中使用该类型。usecrate::garden::vegetables::Asparagususe crate::garden::vegetables::Asparagus;Asparagus

在这里,我们创建了一个名为 的二进制 crate 来说明这些规则。 crate 的目录(也称为 )包含这些文件和 目录:backyardbackyard

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

在本例中,crate 根文件是 src/main.rs,它包含:

文件名: src/main.rs

use crate::garden::vegetables::Asparagus;

pub mod garden;

fn main() {
    let plant = Asparagus {};
    println!("I'm growing {plant:?}!");
}

这行告诉编译器包含它在 src/garden.rs 中找到的代码,即:pub mod garden;

文件名: src/garden.rs

pub mod vegetables;

这里,表示 src/garden/vegetables.rs 中的代码是 也包括。该代码为:pub mod vegetables;

#[derive(Debug)]
pub struct Asparagus {}

现在让我们深入了解这些规则的细节并在实践中演示它们!

模块让我们在一个 crate 中组织代码,以实现可读性和易于重用。 模块还允许我们控制项目的隐私,因为 module 默认为 private 的。私有项目是内部实现细节 不可用于外部使用。我们可以选择制作 modules 和 items 在它们中 public,这使它们暴露以允许外部代码使用和依赖 在他们身上。

例如,让我们编写一个库 crate,它提供 餐厅。我们将定义函数的签名,但保留它们的主体 empty 来专注于代码的组织,而不是 实施一家餐厅。

在餐饮业中,餐厅的某些部分称为前台,其他部分称为后台。前台是 客户是;这包括主机就座客户、服务器所在的位置 订单和付款,调酒师制作饮料。后台是 厨师和厨师在厨房工作,洗碗机打扫卫生,经理们负责 行政工作。

要以这种方式构建我们的 crate,我们可以将其函数组织成嵌套的 模块。通过运行 创建名为 的新库。然后将示例 7-1 中的代码输入到 src/lib.rs 中,以 定义一些模块和函数签名;此代码是 front of house 部分。restaurantcargo new restaurant --lib

文件名: src/lib.rs

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() {}
    }
}

示例 7-1 front_of_house:包含其他 然后包含函数的模块

我们使用关键字后跟模块名称来定义一个模块 (在本例中为 )。然后,模块的主体进入 curly 内部 括弧。在模块中,我们可以放置其他模块,就像在本例中使用 modules 和 .模块还可以保存其他 项目,比如结构、枚举、常量、特征,以及 清单 中所示 7-1 - 函数。modfront_of_househostingserving

通过使用模块,我们可以将相关定义组合在一起并命名原因 他们是相关的。使用此代码的程序员可以根据 组,而不必通读所有定义,这让事情变得更容易 以查找与其相关的定义。程序员添加新功能 到此代码将知道将代码放置在何处以保持程序井井有条。

前面我们提到了 src/main.rssrc/lib.rs 被称为 crate 根。他们之所以得名,是因为这两个内容中的任何一个 文件组成一个名为 crate 模块结构根目录的模块, 称为模块树crate

示例 7-2 显示了示例 7-1 中结构的模块树。

crate
 └── front_of_house
     ├── hosting
     │   ├── add_to_waitlist
     │   └── seat_at_table
     └── serving
         ├── take_order
         ├── serve_order
         └── take_payment

示例 7-2:清单中代码的模块树 7-1

此树显示某些模块如何嵌套在其他模块中;例如,嵌套在 .树还显示一些模块 是同级,这意味着它们在同一模块中定义; 和 是 中定义的同级。如果模块 A 为 包含在模块 B 中,我们说模块 A 是模块 B 的子项,并且 该模块 B 是模块 A 的父级。请注意,整个模块树 根植于名为 的隐式模块下。hostingfront_of_househostingservingfront_of_housecrate

模块树可能会提醒您 计算机;这是一个非常贴切的比较!就像文件系统中的目录一样, 您可以使用 Modules 来组织您的代码。就像目录中的文件一样,我们 需要一种方法来找到我们的模块。

本文档由官方文档翻译而来,如有差异请以官方英文文档(https://doc.rust-lang.org/)为准