Ошибка Rust E0603: function, struct or module is private (приватный элемент)
E0603 — на самом деле хорошая новость: Rust нашёл функцию, структуру или модуль, которые ты просил. Просто ему нельзя отдать их тебе, потому что элемент приватный. Всё в модуле Rust приватно, пока ты не скажешь иначе, поэтому эту ошибку видит почти каждый, кто впервые разбивает код на модули. Обычно исправление — одно слово, pub. Эта страница — часть руководства по ошибкам компилятора.
Сообщение об ошибке
Заголовок раздела «Сообщение об ошибке»error[E0603]: function `apply_discount` is private --> src/main.rs:8:26 |8 | let total = billing::apply_discount(200); // ❌ private | ^^^^^^^^^^^^^^ private function |note: the function `apply_discount` is defined here --> src/main.rs:2:5 |2 | fn apply_discount(price: u32) -> u32 { // no `pub` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^Компилятор пишет: функция apply_discount приватная, и показывает, где она определена. Вот программа, которая выдаёт эту ошибку:
mod billing { fn apply_discount(price: u32) -> u32 { // no `pub` price * 90 / 100 }}
fn main() { let total = billing::apply_discount(200); // ❌ private println!("Total: {total}");}Комментарии отмечают: у функции нет pub, а вызов обращается к приватному элементу. Первая строка называет вид элемента, поэтому у ошибки есть несколько вариантов:
| Что говорит первая строка | Что обычно пошло не так | Исправление |
|---|---|---|
function `apply_discount` is private |
у fn нет pub |
Исправление 1 или 2 |
struct `Order` is private |
у struct нет pub |
Исправление 1 |
module `prices` is private |
у какого-то mod на пути нет pub |
Исправление 1 или 3 |
function import `price_of` is private |
ты добрался до элемента через обычный use внутри другого модуля |
Исправление 3 |
tuple struct constructor `Percent` is private |
структура pub, а её поля — нет |
Исправление 4 |
Что это значит
Заголовок раздела «Что это значит»Прочитай две стрелки. Первая (строка 8) — место, где ты используешь элемент. Стрелка из note: (строка 2) — место, где элемент определён. Rust говорит: «Я вижу apply_discount вон там, но её никто не пометил как общую, так что код снаружи billing не может её вызвать».
Файл не важен. Если billing лежит в src/billing.rs, ошибка будет такой же, просто note: укажет на src/billing.rs:1:1.
Представь офисное здание. Каждый модуль — комната с дверью. Приватный элемент — инструмент, который остаётся в своей комнате: им могут пользоваться те, кто внутри (и в комнатах поменьше внутри неё), но никто из коридора. pub ставит инструмент на полку в коридоре.
Почему Rust так делает
Заголовок раздела «Почему Rust так делает»В Rust всё приватно по умолчанию: функции, структуры, поля структур, модули и даже импорты use. Делиться ты решаешь сам, через pub. Урок Модули показывает это с самого начала.
Причина — безопасность для тебя в будущем. Всё, что pub, становится обещанием: другой код может от этого зависеть, и изменение может его сломать. Приватные элементы можно свободно менять, переименовывать или удалять, а компилятор гарантирует, что снаружи на них никто не полагается. Маленькие вспомогательные функции остаются вспомогательными, а структура может защищать свои правила (например, «баланс никогда не бывает отрицательным»), потому что никто другой не трогает её поля напрямую.
Ещё одна деталь: приватность проверяется для каждого шага пути. shop::prices::price_of работает, только если тебе виден prices и виден price_of. pub fn внутри приватного модуля всё равно недоступна.
Как исправить
Заголовок раздела «Как исправить»Исправление 1: добавь pub к элементу (и к каждому модулю на пути)
Заголовок раздела «Исправление 1: добавь pub к элементу (и к каждому модулю на пути)»Выбирай это, когда элемент должен использоваться снаружи своего модуля. Чаще всего это правильное исправление:
mod billing { pub fn apply_discount(price: u32) -> u32 { price * 90 / 100 }}
fn main() { let total = billing::apply_discount(200); println!("Итого: {total}");}Вывод:
Итого: 180Если ошибка говорит module `prices` is private, pub не хватает у модуля: напиши pub mod prices { ... }. Об этом случае rustc сообщает и второй подписью, function `price_of` is not publicly re-exported (функция не реэкспортирована публично). Если модуль в отдельном файле, pub ставится в строке объявления: pub mod prices;.
Для структуры pub struct Order делает публичным тип, но каждому полю всё равно нужен свой pub, если код снаружи должен его читать или менять. Подробнее в исправлении 4.
Исправление 2: pub(crate) или pub(super) для «только моего кода»
Заголовок раздела «Исправление 2: pub(crate) или pub(super) для «только моего кода»»Выбирай это, когда элементом нужно делиться, но не со всем миром. У pub есть несколько уровней, от меньшего к большему:
| Ты пишешь | Кто может пользоваться элементом |
|---|---|
| ничего (приватный) | его собственный модуль и модули внутри него |
pub(super) |
ещё и родительский модуль |
pub(crate) |
любой код в этом крейте |
pub |
все, включая другие крейты, которые зависят от твоего |
В программе (бинарном крейте) pub(crate) и pub ведут себя почти одинаково. Разница важна в библиотеке: pub(crate) позволяет твоим модулям делиться вспомогательной функцией, не делая её частью публичного API библиотеки.
mod shop { // Это может вызвать любой код в этом крейте. pub(crate) fn store_name() -> &'static str { "Ferris Fruits" }
pub mod receipt { // Эту вспомогательную функцию может вызвать только родительский модуль, `shop`. pub(super) fn line(item: &str, price: u32) -> String { format!("{item}: {price}") } }
pub fn print_receipt(items: &[(&str, u32)]) { println!("== {} ==", store_name()); for (item, price) in items { println!("{}", receipt::line(item, *price)); } }}
fn main() { println!("Добро пожаловать в {}", shop::store_name()); shop::print_receipt(&[("яблоко", 3), ("груша", 5)]); // shop::receipt::line("яблоко", 3); // ❌ E0603: `line` — только для `shop`}Вывод:
Добро пожаловать в Ferris Fruits== Ferris Fruits ==яблоко: 3груша: 5Убери // перед последней строкой — и получишь function `line` is private, хотя там написано pub(super). main не родитель receipt, поэтому дверь остаётся закрытой.
Исправление 3: реэкспортируй через pub use
Заголовок раздела «Исправление 3: реэкспортируй через pub use»Выбирай это, когда внутренний модуль — деталь реализации, но один элемент внутри него должен быть легко доступен. Оставь модуль приватным и выложи элемент по более короткому публичному пути:
mod shop { mod prices { // остаётся приватным: как хранятся цены — дело магазина pub fn price_of(item: &str) -> u32 { if item == "яблоко" { 3 } else { 5 } } }
pub use prices::price_of; // теперь доступно как shop::price_of}
fn main() { println!("Яблоко стоит {}", shop::price_of("яблоко")); println!("Груша стоит {}", shop::price_of("груша"));}Вывод:
Яблоко стоит 3Груша стоит 5Это же исправление и для function import `price_of` is private. Обычный use prices::price_of; внутри shop — ярлык только для shop; как и всё остальное, use приватен. pub use превращает ярлык в публичную дверь. Большие крейты делают так постоянно, поэтому ты можешь писать serde::Serialize, не зная, в каком внутреннем модуле он лежит.
Исправление 4: добавь pub fn new вместо публичных полей
Заголовок раздела «Исправление 4: добавь pub fn new вместо публичных полей»Выбирай это, когда ошибка касается полей структуры или её конструктора. Здесь структура pub, а её поле — нет:
mod billing { pub struct Percent(u8);}
fn main() { let discount = billing::Percent(150); // ❌ private constructor}error[E0603]: tuple struct constructor `Percent` is private --> src/main.rs:6:29 |2 | pub struct Percent(u8); | -- a constructor is private if any of the fields is private...6 | let discount = billing::Percent(150); // ❌ private constructor | ^^^^^^^ private tuple struct constructor |help: consider making the field publicly accessible |2 | pub struct Percent(pub u8); | +++Компилятор объясняет: конструктор приватный, если приватно хоть одно поле. rustc предлагает pub u8, и для простого типа с данными это нормально. Но посмотри на значение: 150 процентов! Часто приватное поле и есть смысл. Модуль хочет проверить значение до того, как появится Percent. Дай ему публичный конструктор, который делает проверку:
mod billing { pub struct Percent(u8);
impl Percent { pub fn new(value: u8) -> Option<Percent> { if value <= 100 { Some(Percent(value)) } else { None } }
pub fn apply(&self, price: u32) -> u32 { price * (100 - self.0 as u32) / 100 } }}
fn main() { let ten = billing::Percent::new(10).unwrap(); println!("200 со скидкой 10%: {}", ten.apply(200)); println!("Можно ли 150%? {}", billing::Percent::new(150).is_some());}Вывод:
200 со скидкой 10%: 180Можно ли 150%? falseТеперь каждый Percent во всей программе гарантированно лежит между 0 и 100, и в apply никогда не будет антипереполнения. Помни, что самой new тоже нужен pub; приватный метод даёт другую ошибку, E0624 (associated function `new` is private). Подробнее о конструкторах — в уроке Структуры.
Где это обычно встречается
Заголовок раздела «Где это обычно встречается»Пакет, в котором есть и main.rs, и lib.rs. Это два отдельных крейта, и main.rs использует библиотеку снаружи. Если в lib.rs написано mod inventory;, то use shop::inventory::count_items; в main.rs не компилируется:
error[E0603]: module `inventory` is private --> src/main.rs:1:11 |1 | use shop::inventory::count_items; | ^^^^^^^^^ private moduleМодуль inventory приватный. Исправление: напиши pub mod inventory; в lib.rs и pub fn count_items в модуле. Осторожно: pub(crate) здесь не хватит. Для библиотеки main.rs — другой крейт, поэтому pub(crate) fn всё равно даёт function `count_items` is private. Подробнее в уроке Cargo и крейты.
Приватные поля структуры. У них свои коды ошибок. Создание структуры с приватным полем даёт E0451, а чтение такого поля — E0616:
error[E0451]: field `balance` of struct `Account` is privateerror[E0616]: field `balance` of struct `Account` is privateОба сообщения говорят, что поле balance структуры Account приватное. Исправление: тот же выбор, что в исправлении 4. Добавь полю pub, если менять его может кто угодно, или оставь его приватным и добавь pub fn new(...) плюс геттер вроде pub fn balance(&self) -> u64.
Функция, найденная в исходниках крейта. Ты читаешь крейт на GitHub, находишь идеальную вспомогательную функцию — и получаешь E0603 при вызове. Если авторы крейта не сделали её pub, пользоваться ею нельзя, и они могут поменять её в любой момент. Поищи публичный способ сделать то же самое на странице крейта на docs.rs; там видны только элементы с pub.
Связанные ошибки
Заголовок раздела «Связанные ошибки»- E0432:
unresolved import. Элемент вообще не нашёлся. При E0603 он нашёлся, но приватный. - E0433:
failed to resolve— путь в твоём коде, который никуда не ведёт. - E0451 и E0616: поле структуры приватное (при создании структуры или при чтении поля).
- E0624: метод или ассоциированная функция (например,
new) приватные. - Все ошибки компилятора: полный список страниц с ошибками.
Проверь себя
Заголовок раздела «Проверь себя»Проверь себя
1. Есть mod shop { mod prices { pub fn price_of() {} } }, и main вызывает shop::prices::price_of(). Какую ошибку ты получишь?
2. Вспомогательная функция в src/lib.rs помечена pub(crate). Может ли src/main.rs из того же пакета её вызвать?
3. У pub struct Email(String); поле намеренно приватное. Как по-доброму дать другим модулям её создавать?
Результат: 0 / 3
Что почитать
Заголовок раздела «Что почитать»- E0603 — список кодов ошибок Rust (на английском): официальное объяснение с минимальным примером.
- Пути для ссылки на элемент в дереве модулей — книга «Язык программирования Rust» (перевод на русский): как открывать пути через
pubи какpubработает со структурами и перечислениями. - Введение путей в область видимости с помощью ключевого слова use — книга «Язык программирования Rust» (перевод на русский): реэкспорт имён через
pub use. - Видимость — Rust на примерах (перевод на русский):
pub,pub(crate),pub(super)иpub(in path)в одном запускаемом примере. - Visibility and privacy — The Rust Reference (на английском): точные правила, если нужна полная картина.