В чём различие между объявлением модуля и импортом его имени через use в Rust?
mod объявляет модуль: он создаёт модульный элемент и подключает его содержимое, заданное inline или во внешнем файле. use не создаёт модуль — он только добавляет выбранный путь или имя в область видимости, обычно чтобы обращаться к уже существующему элементу короче.
Модульная система Rust решает две задачи: разделяет код по пространствам имён и позволяет явно управлять доступом к элементам. Отдельно от структуры модулей нужен механизм коротких ссылок на уже объявленные элементы, поэтому объявление модуля и импорт имени имеют разные операции и семантику.
Такое разделение позволяет изменить способ обращения к элементу через use, не меняя саму структуру модулей. Оно также предотвращает ошибочное представление, будто импорт копирует или перемещает код в текущий модуль.
Если перепутать mod и use, можно получить либо повторное объявление модуля, либо ошибку поиска имени. Например, use не подключит содержимое файла с модулем, если этот модуль нигде не объявлен через mod, а повторное объявление уже существующего модуля создаст конфликт имён.
Неверное понимание особенно опасно при реорганизации проекта: путь может стать доступным локально, но это не означает, что соответствующий элемент стал частью публичного API или получил новую видимость.
Запись mod payments создаёт дочерний модуль payments. Если тело модуля указано в фигурных скобках, оно находится inline; если тело отсутствует, Rust ищет содержимое модуля во внешнем файле по правилам структуры crate. В обоих случаях mod формирует элемент модульного дерева и инициирует компиляцию его содержимого.
Запись use payments::charge создаёт локальное связывание имени charge с уже существующей функцией. Она не создаёт копию функции, не меняет её исходное расположение и не делает её доступной другим модулям.
use действует в области видимости, где написан. Обычный use по умолчанию является частным связыванием; чтобы переэкспортировать имя через публичный интерфейс модуля, применяется pub use. Это меняет доступность пути для внешнего кода, но не превращает импорт в новое объявление модуля.
Минимальный пример:
Здесь mod payments объявляет модуль и функцию внутри него, а use payments::charge лишь позволяет вызвать эту функцию по короткому имени charge. Без use вызов всё равно был бы возможен через полный путь payments::charge.
Команда вынесла реализацию платежей из основного файла в отдельный модуль. Вариант с полными путями, например обращение через payments::charge, прозрачен и снижает риск конфликта имён, но делает код более многословным. Вариант с локальным use улучшает читаемость, однако добавляет зависимость от текущей области видимости и может скрыть происхождение имени.
Если функция должна быть частью публичного API фасадного модуля, простого use недостаточно: внешний код его не увидит. В такой ситуации выбирают pub use, потому что он сохраняет внутреннюю реализацию в отдельном модуле, но предоставляет потребителям стабильный внешний путь. Результат — модуль объявлен один раз, внутренние вызовы остаются короткими, а публичный интерфейс контролируется явно.
Может ли use сделать приватную функцию доступной внешнему коду?
Нет. use только создаёт дополнительное имя для элемента в текущей области видимости; проверка приватности исходного элемента сохраняется. Даже если импорт записан в публичном модуле, обычный use не превращает приватную функцию в часть внешнего API.
Для переэкспорта применяется pub use, но и он не отменяет ограничения приватности: переэкспортировать можно только элемент, доступный из места переэкспорта по правилам Rust.
Создаёт ли mod новый модуль при каждом обращении к его имени?
Нет. Объявление mod выполняется как объявление элемента модульного дерева, а не как операция загрузки при каждом использовании. После объявления к модулю можно обращаться по путям, импортировать его имена через use и передавать его элементы в другие выражения согласно правилам видимости.
Поэтому повторное объявление того же модуля в одной области видимости не является способом повторно импортировать его. Для дополнительного обращения используется путь или use.
Что произойдёт, если заменить use на mod только ради короткого имени?
Это будет не эквивалентная замена. mod потребует нового объявления модуля и его содержимого, а при наличии уже объявленного модуля обычно приведёт к конфликту или изменит структуру crate. Кроме того, Rust начнёт рассматривать указанное имя как модульный элемент, а не как локальный псевдоним существующей функции, типа или другого пути.
Для сокращения существующего пути нужен use; для создания или подключения модуля — mod. Разделение этих ролей сохраняет однозначность структуры программы и области видимости имён.