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

Разделение модулей на файлы

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

Возьмём для примера код из листинга 7-17 с несколькими модулями ресторана. Вынесем модули в отдельные файлы, чтобы не держать их все в файле корня крейта. Здесь корень крейта — src/lib.rs, но так же можно поступить и с бинарным крейтом, у которого корень — src/main.rs.

Сначала вынесем в отдельный файл модуль front_of_house. Удалите код внутри фигурных скобок модуля front_of_house, оставив только объявление mod front_of_house;, чтобы в src/lib.rs остался код из листинга 7-21. Учтите: он не скомпилируется, пока мы не создадим файл src/front_of_house.rs из листинга 7-22.

Файл: src/lib.rs
mod front_of_house;

pub use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
}
Листинг 7-21: Объявляем модуль front_of_house, тело которого будет в src/front_of_house.rs

Затем перенесите код, который был в фигурных скобках, в новый файл src/front_of_house.rs, как в листинге 7-22. Компилятор знает, что искать нужно в этом файле, потому что в корне крейта он встретил объявление модуля с именем front_of_house.

Файл: src/front_of_house.rs
pub mod hosting {
    pub fn add_to_waitlist() {}
}
Листинг 7-22: Определения внутри модуля front_of_house в файле src/front_of_house.rs

Обратите внимание: загружать файл объявлением mod нужно лишь один раз во всём дереве модулей. Как только компилятор знает, что файл — часть проекта (и знает, где в дереве модулей находится его код, — по тому, где стоит инструкция mod), остальные файлы проекта должны обращаться к коду загруженного файла по пути к месту его объявления, как описано в разделе «Пути к элементам в дереве модулей». Иными словами, mod — это не операция «include», которая может быть вам знакома по другим языкам программирования.

Теперь вынесем в отдельный файл модуль hosting. Тут всё немного иначе: hosting — дочерний модуль front_of_house, а не корневого модуля. Файл для hosting положим в новую папку, названную по его предкам в дереве модулей, — в данном случае src/front_of_house.

Чтобы начать перенос hosting, оставим в src/front_of_house.rs только объявление модуля hosting:

Файл: src/front_of_house.rs
pub mod hosting;

Затем создадим папку src/front_of_house и в ней файл hosting.rs с определениями из модуля hosting:

Файл: src/front_of_house/hosting.rs
pub fn add_to_waitlist() {}

Если бы мы положили hosting.rs прямо в папку src, компилятор решил бы, что код из hosting.rs относится к модулю hosting, объявленному в корне крейта, а не к дочернему модулю front_of_house. Благодаря правилам, по которым компилятор ищет код модулей в файлах, структура папок и файлов близко повторяет дерево модулей.

Альтернативные пути к файлам

Мы рассмотрели самые идиоматичные пути к файлам, которые использует компилятор Rust, но Rust поддерживает и более старый стиль. Для модуля front_of_house, объявленного в корне крейта, компилятор ищет код в:

  • src/front_of_house.rs (тот вариант, что мы разобрали);
  • src/front_of_house/mod.rs (старый стиль, по-прежнему поддерживается).

Для модуля hosting, который является подмодулем front_of_house, компилятор ищет код в:

  • src/front_of_house/hosting.rs (тот вариант, что мы разобрали);
  • src/front_of_house/hosting/mod.rs (старый стиль, по-прежнему поддерживается).

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

Главный недостаток стиля с файлами mod.rs в том, что в проекте накапливается много файлов с одинаковым именем mod.rs, и когда несколько из них одновременно открыты в редакторе, в них легко запутаться.

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

Заметьте, что инструкция pub use crate::front_of_house::hosting в src/lib.rs тоже не изменилась, и use никак не влияет на то, какие файлы компилируются в составе крейта. Модули объявляет ключевое слово mod, а код модуля Rust ищет в файле с тем же именем, что и модуль.

Итоги

Rust позволяет разбить пакет на несколько крейтов, а крейт — на модули. Чтобы обратиться из одного модуля к элементу, определённому в другом, указывают абсолютный или относительный путь. Пути можно подключить в область видимости инструкцией use, чтобы при многократном использовании элемента в этой области писать путь покороче. Код модуля по умолчанию приватный, но определения можно сделать публичными, добавив ключевое слово pub.

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