Какое правило приватности Rust определяет доступ дочернего модуля к приватному элементу родителя?
Дочерний модуль имеет доступ к приватным элементам родительского модуля. В Rust приватность действует для модуля, где элемент объявлен, и всех его потомков. Обратное направление не работает: родительский модуль не получает доступ к приватным элементам дочернего.
Модульная система Rust решает задачу инкапсуляции на уровне пространства имён и структуры crate, не требуя классов. Такой подход позволяет оставить внутренние детали доступными для реализации подмодулей, но скрыть их от внешнего кода.
Правило доступа к потомкам удобно для разбиения крупной реализации на несколько вложенных модулей. При этом публичный API можно явно ограничивать с помощью pub, pub(crate) и других форм видимости.
Предположим, родительский модуль хранит вспомогательную функцию, а вложенный модуль реализует часть его логики. Если бы приватный элемент был доступен только непосредственно в модуле объявления, реализацию пришлось бы искусственно объединять в одном модуле или делать внутреннюю функцию публичной.
Если же приватность распространялась бы вниз и вверх одинаково, родитель получил бы неявный доступ к деталям дочерних модулей. Это нарушило бы направление зависимости и усложнило бы контроль API.
Видимость приватного элемента определяется относительно модуля объявления. Элемент без pub доступен внутри этого модуля и внутри всех его дочерних модулей, включая более глубоких потомков.
В примере inner может вызвать outer::secret, потому что inner является потомком outer. Однако outer не может напрямую обратиться к приватному элементу, объявленному внутри inner, а соседний модуль также не получает такого доступа.
Для кода за пределами crate обычного приватного элемента доступ закрыт. Если доступ нужен всему crate, применяют pub(crate); если только родителю — pub(super); pub делает элемент доступным согласно обычным правилам публичного API, включая ограничения приватных компонентов типов.
Важно отличать доступность элемента от разрешения пути к нему. Даже если функция приватна для внешнего кода, её можно вызывать из дочернего модуля через подходящий путь вроде super::secret(). Но сам дочерний модуль тоже должен быть доступен вызывающему коду, если его функция экспортируется наружу.
В библиотеке есть модуль разбора конфигурации и вложенный модуль валидации. Валидации нужен приватный нормализатор родительского модуля, но пользователям библиотеки этот нормализатор не должен быть доступен.
Первый вариант — объявить нормализатор как pub. Он прост, но расширяет публичный API и позволяет клиентам зависеть от внутренней детали. Второй вариант — переместить нормализатор в модуль валидации, но тогда общая логика окажется в менее подходящем месте и может усложнить повторное использование.
Рациональный выбор — оставить нормализатор приватным в родительском модуле и вызывать его из дочернего. Это сохраняет инкапсуляцию для внешнего кода, одновременно позволяя организовать реализацию по вложенным модулям. Если доступ потребуется другим частям crate, видимость можно расширить до pub(crate), но делать это следует только при наличии такой зависимости.
Вопрос: Получает ли родительский модуль доступ к приватным элементам дочернего модуля?
Ответ: Нет. Направление доступа одностороннее: приватность дочернего элемента разрешает обращение из самого дочернего модуля и его потомков, но не из родителя. Родитель должен использовать публичный или явно ограниченно-публичный интерфейс дочернего модуля.
Вопрос: Может ли соседний модуль обратиться к приватному элементу другого модуля через путь к нему?
Ответ: Нет, одного знания пути недостаточно. Соседний модуль не является ни модулем объявления, ни его потомком, поэтому приватный элемент для него недоступен. Обычно связь оформляют через функцию с подходящей видимостью, например pub(super) или pub(crate), в зависимости от требуемой границы.
Вопрос: Делает ли pub(crate) элемент доступным внешним пользователям библиотеки?
Ответ: Нет. pub(crate) открывает элемент внутри текущего crate, но не за его пределами. Это полезно для взаимодействия внутренних модулей без включения элемента в публичный API библиотеки; внешняя программа не сможет обратиться к нему напрямую.