Разделение модулей по разным файлам
До сих пор все примеры в этой главе определяли несколько модулей в одном файле. Когда модули становятся большими, вы можете захотеть перенести их определения в отдельный файл, чтобы по коду было легче перемещаться.
Например, начнем с кода из листинга 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.
mod front_of_house;
pub use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
front_of_house, тело которого будет находиться в src/front_of_house.rsЗатем поместите код, который был внутри фигурных скобок, в новый файл с именем
src/front_of_house.rs, как показано в листинге 7-22. Компилятор знает, что
нужно искать в этом файле, потому что он встретил объявление модуля с именем
front_of_house в корне крейта.
pub mod hosting {
pub fn add_to_waitlist() {}
}
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:
pub mod hosting;
Затем мы создаем каталог src/front_of_house и файл hosting.rs, который
будет содержать определения, сделанные в модуле hosting:
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.
В следующей главе мы рассмотрим некоторые структуры данных коллекций из стандартной библиотеки, которые можно использовать в вашем аккуратно организованном коде.