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

Определение модулей для управления областью видимости и доступностью

В этом разделе мы поговорим о модулях и других частях системы модулей, а именно о путях, которые позволяют именовать элементы; ключевом слове 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, и он содержит:

Filename: 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, а это:

Filename: 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, чтобы определить некоторые модули и сигнатуры функций; этот код представляет раздел передней части зала.

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: Модуль 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
Listing 7-2: Дерево модулей для кода в Листинге 7-1

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

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