Зачем модулю Rust нужен модификатор pub(crate), если pub уже делает элемент доступным?
pub(crate) открывает элемент для всех модулей текущего crate, но не включает его в публичный API библиотеки для внешних crate. Это компромисс между полной приватностью и глобальной публичностью: внутренние части проекта могут взаимодействовать, а внешние пользователи не получают доступ к детали реализации.
Модульная система Rust изначально решает две связанные задачи: разделение кода на компоненты и контроль границ API. Поэтому доступ к элементам по умолчанию ограничен, а расширение видимости выполняется явно.
Одной только приватности недостаточно для крупного crate: отдельным модулям часто требуется общий внутренний контракт. pub(crate) позволяет предоставить такой контракт внутри библиотеки, не превращая его в обязательство перед внешними пользователями.
Представим внутренний кэш, фабрику или служебный тип, которым пользуются несколько модулей crate. Если оставить элемент приватным, соседние модули не смогут обращаться к нему напрямую. Если объявить его через pub, внешний код тоже получит доступ, а изменение этого элемента усложнит поддержку совместимости публичного API.
Неверный выбор видимости либо мешает внутренней декомпозиции, либо преждевременно раскрывает детали реализации. В библиотеке это особенно важно: внешний API обычно поддерживается дольше, чем внутренние связи между модулями.
pub(crate) означает видимость в пределах текущего crate. Внешний crate не может обратиться к такому элементу через имя библиотеки, даже если сам модуль и путь к нему доступны.
Здесь service может использовать storage::reset, потому что оба модуля находятся в одном crate. Код, подключающий эту библиотеку как зависимость, вызвать storage::reset не сможет.
Видимость каждого звена пути также имеет значение: публичный элемент не становится доступным через приватный родительский модуль. Кроме pub(crate), Rust предоставляет более узкие формы вроде pub(super) и pub(in path), когда доступ нужно ограничить конкретной частью иерархии.
Компромисс pub(crate) состоит в том, что он не заменяет полноценный дизайн API. Любой модуль crate получает возможность использовать элемент, поэтому изменение его сигнатуры может потребовать исправлений внутри всего crate. Однако внешняя совместимость при этом не связывается с этой деталью.
В библиотеке есть модуль разбора конфигурации и модуль диагностики. Диагностике нужен внутренний конструктор события, но пользователи библиотеки должны создавать события только через высокоуровневый API.
Вариант с приватным конструктором не подходит: соседний модуль не сможет его вызвать без дополнительного публичного посредника. Вариант с pub технически работает, но раскрывает внутреннюю структуру и позволяет внешнему коду зависеть от неё.
Выбирается pub(crate): конструктор доступен обоим внутренним модулям, но не считается частью внешнего API. В результате внутренние компоненты остаются связными там, где это необходимо, а реализацию можно менять без обязательной поддержки внешних вызовов.
1. Будет ли pub(crate) доступен интеграционным тестам?
Нет, если речь идёт о стандартных интеграционных тестах в каталоге tests. Каждый такой тест компилируется как отдельный crate и поэтому не входит в область действия pub(crate). Модульные тесты, объявленные внутри самого crate, обычно имеют доступ к его crate-видимым элементам.
Это различие важно при организации тестов: проверка внутреннего контракта обычно размещается в модульных тестах, а интеграционные тесты должны использовать только доступный пользователю API.
2. Достаточно ли сделать элемент pub(crate), если его родительский модуль приватен?
Нет. Доступность ограничивается самым узким звеном пути. Если внешний для текущего модуля путь проходит через приватный родительский модуль, один лишь pub(crate) у вложенного элемента не сделает весь путь доступным.
Поэтому видимость анализируют не только у конечной функции или типа, но и у модулей, через которые к ним обращаются.
3. Можно ли переэкспортировать элемент с pub(crate) как часть публичного API?
Нет. Публичный переэкспорт не должен раскрывать элемент, который доступен только внутри crate. Попытка сделать такой элемент доступным внешним пользователям через pub use приводит к ошибке видимости.
Переэкспорт может изменить путь доступа, но не отменяет исходные ограничения. Чтобы элемент стал частью внешнего API, его собственная видимость и видимость всех необходимых частей пути должны это разрешать.