Определение модулей для контроля видимости и конфиденциальности

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

Модули позволяют организовывать код внутри крейта по группам для удобства чтения и простого повторного использования. Модули также контролируют конфиденциальность (privacy) элементов: определяют может элемент использоваться внешним кодом, быть публичным (public) или является деталями внутренней реализации и недоступен для внешнего использования, т.е. является приватным (private).

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

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

Чтобы структурировать крейт аналогично тому, как работает настоящий ресторан, можно организовать размещение функций во вложенных модулях. Создадим новую библиотеку (библиотечный крейт) с именем restaurant выполнив команду cargo new --lib restaurant; затем вставим код из листинга 7-1 в файл src/lib.rs для определения некоторых модулей и сигнатур функций.

Файл: 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() {}
    }
}

Листинг 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

Листинг 7-2: Дерево модулей для для структуры модулей приведённой в коде в листинге 7-1

Это дерево показывает, как некоторые из модулей вкладываются друг в друга (например, hosting находится внутри front_of_house). Дерево также показывает, что некоторые модули являются братьями (siblings) друг для друга, то есть они определены в одном модуле (hosting и serving - братья которые определены внутри front_of_house). Продолжая метафору с семьёй: если модуль A содержится внутри модуля B, мы говорим, что модуль A является потомком (child) модуля B, а модуль B является родителем (parent) модуля A. Обратите внимание, что корнем (отцом, главным предком) всего дерева модулей является неявный модуль с именем crate.

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