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 для бинарного крейта).
  • Объявление модулей: в файле корня крейта можно объявлять новые модули; допустим, вы объявляете модуль “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, и он содержит:

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

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

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

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

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

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

Чтобы структурировать наш крейт таким образом, мы можем организовать его функции во вложенные модули. Создайте новую библиотеку с именем 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. Модули также могут содержать определения других элементов, например структур, 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
Listing 7-2: Дерево модулей для кода из листинга 7-1

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

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