Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Control Scope and Privacy with Modules

В этом разделе мы поговорим о модулях и других частях системы модулей, а именно: о путях, которые позволяют именовать элементы; о ключевом слове use, которое подключает путь к области видимости; о ключевом слове pub, которое делает элементы общедоступными. Мы также обсудим ключевое слово as, внешние пакеты и оператор *.

Шпаргалка по модулям

Перед тем как вдаваться в детали работы с модулями и путями, мы дадим краткий обзор того, как модули, пути и ключевые слова use и pub работают в компиляторе, и как большинство разработчиков организуют свой код. В этой главе мы рассмотрим всё это на примерах, а пока что у нас есть удобный момент, чтобы дать выжимку о том, как работают крейты.

  • Start from the crate root: When compiling a crate, the compiler first looks in the crate root file (usually src/lib.rs for a library crate and src/main.rs for a binary crate) for code to compile.
  • Объявление модулей. В файле корня крейта вы можете объявлять новые модули. Скажем, вы объявляете модуль “garden” с помощью mod garden;. Компилятор будет искать код модуля в следующих местах:
    • В этом же файле между фигурных скобок, которые заменяют точку с запятой после mod garden
    • В файле src/garden.rs
    • В файле src/garden/mod.rs
  • Объявление подмодулей. В любом файле, кроме корня крейта, вы можете объявлять подмодули. К примеру, вы можете объявить mod vegetables; в src/garden.rs. Компилятор будет искать код подмодуля в каталоге с именем родительского модуля в следующих местах:
    • В этом же файле, сразу после mod vegetables и между фигурных скобок, которые заменяют точку с запятой
    • В файле src/garden/vegetables.rs
    • В файле src/garden/vegetables/mod.rs
  • Пути к коду в модулях. После того, как модуль станет частью вашего крейта, и если допускают правила приватности, вы можете ссылаться на код в этом модуле из любого места вашего крейта, используя путь к коду. Например, тип Asparagus в подмодуле vegetables модуля garden будет доступен по пути crate::garden::vegetables::Asparagus.
  • Закрытость и открытость. Код в модуле по умолчанию скрыт от его родительских модулей. Чтобы сделать модуль общедоступным, объявите его как pub mod вместо mod. Чтобы сделать элементы общедоступного модуля тоже общедоступными, используйте pub перед их объявлением.
  • The use keyword: Within a scope, the use keyword creates shortcuts to items to reduce repetition of long paths. In any scope that can refer to crate::garden::vegetables::Asparagus, you can create a shortcut with use crate::garden::vegetables::Asparagus;, and from then on you only need to write Asparagus to make use of that type in the scope.

Here, we create a binary crate named backyard that illustrates these rules. The crate’s directory, also named backyard, contains these files and directories:

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

Файл корневого модуля крейта (в нашем случае) — src/main.rs. Вот, что он содержит:

Filename: src/main.rs
use crate::garden::vegetables::Asparagus;

pub mod garden;

fn main() {
    let plant = Asparagus {};
    println!("Здесь растёт {plant:?}!");
}

Строка pub mod garden; говорит компилятору подключить код, имеющийся в src/garden.rs. Вот подключаемый код:

Filename: src/garden.rs
pub mod vegetables;

А здесь pub mod vegetables; указывает, что код в src/garden/vegetables.rs тоже подключён. Вот подключаемый код:

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

Теперь давайте рассмотрим детали этих правил и покажем их в действии!

Группировка родственного кода в модули

Модули позволяют упорядочивать код внутри крейта для удобочитаемости и лёгкого повторного использования. Модули также позволяют нам управлять приватностью элементов, поскольку код внутри модуля по умолчанию является закрытым. Приватные элементы — это внутренние детали реализации, недоступные для внешнего использования. Мы можем сделать модули и элементы внутри них общедоступными, что позволит внешнему коду использовать их и зависеть от них.

В качестве примера, давайте напишем библиотечный крейт предоставляющий функциональность ресторана. Мы определим сигнатуры функций, но оставим их тела пустыми, чтобы сосредоточиться на организации кода, вместо реализации самого ресторана.

In the restaurant industry, some parts of a restaurant are referred to as front of house and others as back of house. Front of house is where customers are; this encompasses where the hosts seat customers, servers take orders and payment, and bartenders make drinks. Back of house is where the chefs and cooks work in the kitchen, dishwashers clean up, and managers do administrative work.

To structure our crate in this way, we can organize its functions into nested modules. Create a new library named restaurant by running cargo new restaurant --lib. Then, enter the code in Listing 7-1 into src/lib.rs to define some modules and function signatures; this code is the front of house section.

Filename: 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() {}
    }
}
Listing 7-1: A front_of_house module containing other modules that then contain functions

We define a module with the mod keyword followed by the name of the module (in this case, front_of_house). The body of the module then goes inside curly brackets. Inside modules, we can place other modules, as in this case with the modules hosting and serving. Modules can also hold definitions for other items, such as structs, enums, constants, traits, and as in Listing 7-1, functions.

Используя модули, мы можем группировать родственные определения вместе и пояснять, в чём они являются родственными. Программистам будет легче найти необходимую функциональность в сгруппированном коде, вместо того чтобы искать её в одном общем списке. Программисты, добавляющие новые функции в этот код, будут знать, где разместить код для поддержания порядка в программе.

Earlier, we mentioned that src/main.rs and src/lib.rs are called crate roots. The reason for their name is that the contents of either of these two files form a module named crate at the root of the crate’s module structure, known as the module tree.

В Листинге 7-2 показано дерево модулей для структуры модулей, приведённой в коде Листинга 7-1.

crate
 └── front_of_house
     ├── hosting
     │   ├── add_to_waitlist
     │   └── seat_at_table
     └── serving
         ├── take_order
         ├── serve_order
         └── take_payment
Listing 7-2: The module tree for the code in Listing 7-1

Это дерево показывает, как некоторые из модулей вкладываются друг в друга; например, hosting находится внутри front_of_house. Дерево также показывает, что некоторые модули являются “братьями”, то есть они определены в одном модуле; hosting и serving — это “братья”, которые определены внутри front_of_house. Если модуль A содержится внутри модуля B, мы говорим, что модуль A является потомком модуля B, а модуль B является родителем модуля A. Обратите внимание, что родителем всего дерева модулей является неявный модуль с именем crate.

Дерево модулей может напомнить вам дерево файловой системы на компьютере; это очень удачное сравнение! По аналогии с файловой системой, модули используются для организации кода. И так же, как нам надо искать файлы в директориях, нам требуется способ поиска нужных модулей.