Перейти к содержимому

Ошибка 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 всё приватно по умолчанию: функции, структуры, поля структур, модули и даже импорты 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, поэтому дверь остаётся закрытой.

Выбирай это, когда внутренний модуль — деталь реализации, но один элемент внутри него должен быть легко доступен. Оставь модуль приватным и выложи элемент по более короткому публичному пути:

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 private
error[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