Управление областью видимости и приватностью с помощью модулей
В этом разделе мы поговорим о модулях и других частях системы модулей, а
именно о путях, которые позволяют называть элементы; о ключевом слове use,
которое вводит путь в область видимости; и о ключевом слове pub, которое
делает элементы публичными. Мы также обсудим ключевое слово as, внешние
пакеты и оператор glob.
Шпаргалка по модулям
Прежде чем перейти к подробностям о модулях и путях, дадим краткую справку о
том, как модули, пути, ключевое слово use и ключевое слово pub работают в
компиляторе, а также о том, как большинство разработчиков организует свой код.
В этой главе мы пройдем через примеры каждого из этих правил, но сюда удобно
возвращаться как к напоминанию о том, как работают модули.
- Начинайте с корня крейта: при компиляции крейта компилятор сначала ищет код для компиляции в файле корня крейта (обычно src/lib.rs для библиотечного крейта и src/main.rs для бинарного крейта).
- Объявление модулей: в файле корня крейта можно объявлять новые модули;
допустим, вы объявляете модуль “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перед их объявлениями. - Ключевое слово
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 {}
Теперь перейдем к деталям этих правил и покажем их в действии!
Группирование связанного кода в модулях
Модули позволяют организовывать код внутри крейта для читаемости и удобного повторного использования. Модули также позволяют управлять приватностью элементов, потому что код внутри модуля по умолчанию приватен. Приватные элементы – это внутренние детали реализации, недоступные для внешнего использования. Мы можем сделать модули и элементы внутри них публичными, чтобы внешний код мог использовать их и зависеть от них.
В качестве примера напишем библиотечный крейт, который предоставляет функциональность ресторана. Мы определим сигнатуры функций, но оставим их тела пустыми, чтобы сосредоточиться на организации кода, а не на реализации ресторана.
В ресторанной индустрии некоторые части ресторана называют залом, а другие – служебной частью. Зал – это место, где находятся клиенты: сюда входят зоны, где администраторы рассаживают клиентов, официанты принимают заказы и оплату, а бармены готовят напитки. Служебная часть – это место, где повара работают на кухне, посудомойщики убирают, а управляющие выполняют административную работу.
Чтобы структурировать наш крейт таким образом, мы можем организовать его
функции во вложенные модули. Создайте новую библиотеку с именем 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. Модули также могут содержать
определения других элементов, например структур, enum, констант, трейтов и,
как в листинге 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.
Дерево модулей может напомнить вам дерево каталогов файловой системы на вашем компьютере; это очень точное сравнение! Так же как каталоги в файловой системе, модули используются для организации кода. И так же как с файлами в каталоге, нам нужен способ находить наши модули.