Определение модулей для управления областью видимости и доступностью
В этом разделе мы поговорим о модулях и других частях системы модулей, а именно о путях, которые позволяют именовать элементы; ключевом слове use, которое подключает путь в область видимости; и ключевом слове pub для создания публичных элементов. Мы также обсудим ключевое слово as, внешние пакеты и оператор glob.
Шпаргалка по модулям
Прежде чем углубиться в детали модулей и путей, вот краткая справка о том, как работают модули, пути, ключевое слово use и ключевое слово pub в компиляторе, и как большинство разработчиков организуют свой код. Мы будем рассматривать примеры каждого из этих правил в этой главе, но это отличное место для справки о том, как работают модули.
- Начинайте с корня крейта: При компиляции крейта компилятор сначала ищет код для компиляции в корневом файле крейта (обычно src/lib.rs для библиотечного крейта или src/main.rs для бинарного крейта).
- Объявление модулей: В корневом файле крейта вы можете объявлять новые модуules; допустим, вы объявляете модуль “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в модуле garden vegetables будет находиться по путиcrate::garden::vegetables::Asparagus. - Приватное vs публичное: Код внутри модуля по умолчанию приватный для его родительских модулей. Чтобы сделать модуль публичным, объявите его с помощью
pub modвместоmod. Чтобы сделать элементы внутри публичного модуля также публичными, используйтеpubперед их объявлениями. - Ключевое слово
use: Внутри области видимости ключевое словоuseсоздаёт сокращения для элементов, чтобы уменьшить повторение длинных путей. В любой области видимости, которая может ссылаться наcrate::garden::vegetables::Asparagus, вы можете создать сокращение с помощьюuse crate::garden::vegetables::Asparagus;и с тех пор вам нужно будет писать толькоAsparagusдля использования этого типа в области видимости.
Здесь мы создаём бинарный крейт с именем backyard, который иллюстрирует эти правила. Директория крейта, также названная backyard, содержит эти файлы и директории:
backyard
├── Cargo.lock
├── Cargo.toml
└── src
├── garden
│ └── vegetables.rs
├── garden.rs
└── main.rs
Корневой файл крейта в этом случае — src/main.rs, и он содержит:
use crate::garden::vegetables::Asparagus;
pub mod garden;
fn main() {
let plant = Asparagus {};
println!("I'm growing {plant:?}!");
}
Строка pub mod garden; говорит компилятору включить код, который он находит в src/garden.rs, а это:
pub mod vegetables;
Здесь pub mod vegetables; означает, что код в src/garden/vegetables.rs также включается. Этот код:
#[derive(Debug)]
pub struct Asparagus {}
Теперь давайте подробно разберём эти правила и продемонстрируем их в действии!
Группировка связанного кода в модулях
Модули позволяют нам организовывать код внутри крейта для читаемости и простоты повторного использования. Модули также позволяют нам контролировать доступность элементов, потому что код внутри модуля по умолчанию приватный. Приватные элементы — это внутренние детали реализации, недоступные для использования извне. Мы можем выбрать, чтобы сделать модули и элементы внутри них публичными, что открывает их для внешнего кода, чтобы тот мог их использовать и зависеть от них.
В качестве примера давайте напишем библиотечный крейт, который предоставляет функциональность ресторана. Мы определим сигнатуры функций, но оставим их тела пустыми, чтобы сосредоточиться на организации кода, а не на реализации ресторана.
В индустрии общественного питания некоторые части ресторана называются передней частью зала (front of house), а другие — задней частью зала (back of house). Передняя часть зала — это место, где находятся клиенты; это включает в себя, где хостессы рассаживают клиентов, официанты принимают заказы и оплату, а бармены готовят напитки. Задняя часть зала — это место, где повара и кухонные работники работают на кухне, мойщики убирают, а менеджеры занимаются административной работой.
Чтобы структурировать наш крейт таким образом, мы можем организовать его функции во вложенные модули. Создайте новую библиотеку с именем restaurant, выполнив cargo new restaurant --lib. Затем введите код из Листинга 7-1 в 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() {}
}
}
front_of_house, содержащий другие модули, которые, в свою очередь, содержат функцииМы определяем модуль с помощью ключевого слова mod, за которым следует имя модуля (в этом случае front_of_house). Тело модуля затем помещается внутрь фигурных скобок. Внутри модулей мы можем размещать другие модули, как в этом случае с модулями hosting и serving. Модули также могут содержать определения других элементов, таких как структуры, перечисления, константы, типажи и, как в Листинге 7-1, функции.
Используя модули, мы можем группировать связанные определения вместе и обозначать, почему они связаны. Программисты, использующие этот код, могут ориентироваться в коде на основе групп, а не читать все определения, что облегчает поиск определений, актуальных для них. Программисты, добавляющие новую функциональность в этот код, будут знать, где разместить код, чтобы сохранить программу организованной.
Ранее мы упоминали, что src/main.rs и src/lib.rs называются корнями крейта. Причина их названия в том, что содержимое любого из этих двух файлов образует модуль с именем crate в корне структуры модулей крейта, известной как дерево модулей.
Листинг 7-2 показывает дерево модулей для структуры в Листинге 7-1.
crate
└── front_of_house
├── hosting
│ ├── add_to_waitlist
│ └── seat_at_table
└── serving
├── take_order
├── serve_order
└── take_payment
Это дерево показывает, как некоторые модули вложены в другие модули; например, hosting вложен в front_of_house. Дерево также показывает, что некоторые модули являются братьями и сёстрами, то есть они определены в одном и том же модуле; hosting и serving — братья и сёстры, определённые внутри front_of_house. Если модуль A содержится внутри модуля B, мы говорим, что модуль A является потомком модуля B, а модуль B — родителем модуля A. Обратите внимание, что всё дерево модулей коренится под неявным модулем с именем crate.
Дерево модулей может напомнить вам дерево каталогов файловой системы на вашем компьютере; это очень уместное сравнение! Подобно каталогам в файловой системе, вы используете модули для организации своего кода. И подобно файлам в каталоге, нам нужен способ находить наши модули.