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

Ошибка Rust E0616: field of struct is private (приватное поле)

E0616 значит: поле, которое ты запросил, существует, но оно приватно для модуля, где объявлена структура. Держать саму структуру тебе можно, а залезать внутрь — нет. С этим сталкивается каждый, кто впервые выносит структуру в отдельный модуль, и каждый, кто пишет v.len вместо v.len(). Обычно исправление — вызов метода. Эта страница — часть справочника по ошибкам компилятора.

error[E0616]: field `balance` of struct `Account` is private
--> src/main.rs:16:50
|
16 | println!("{} has {}", account.owner, account.balance); // ❌ private field
| ^^^^^^^ private field

Компилятор говорит: поле balance структуры Account приватное. Вот программа, которая выдаёт эту ошибку:

mod bank {
pub struct Account {
pub owner: String,
balance: u64, // private
}
impl Account {
pub fn new(owner: &str) -> Account {
Account { owner: owner.to_string(), balance: 0 }
}
}
}
fn main() {
let account = bank::Account::new("Ferris");
println!("{} has {}", account.owner, account.balance); // ❌ private field
}

У этой ошибки есть несколько разновидностей:

Что ты написал Что говорит rustc Исправление
account.balance field `balance` of struct `Account` is private Исправление 2 или 3
scores.len у Vec field `len` of struct `Vec` is private, плюс help: a method `len` also exists, call it with parentheses Исправление 1
timeout.secs у Duration field `secs` of struct `Duration` is private Исправление 1 (as_secs())
account.balance = 500 та же E0616: запись тоже запрещена Исправление 2

Стрелка показывает на имя поля после точки. account.owner в той же строке в порядке, потому что owner помечено pub. А balance — нет, поэтому читать и менять его может только код внутри mod bank.

Обрати внимание: сама Account — pub. Именно это всех удивляет: если структура публичная, это не делает её поля публичными. Каждое поле получает свой pub или остаётся приватным.

Представь банк. Ты можешь зайти внутрь, и кто угодно может увидеть имя владельца счёта. Но ты не можешь зайти за стойку, открыть сейф и пересчитать деньги. Ты просишь кассира, а кассир действует по правилам банка.

В Rust всё приватно по умолчанию: функции, модули и поля структур тоже (смотри Модули). Приватность определяется модулями, а не типами. Любой код внутри mod bank может трогать balance, а код снаружи — нет.

Смысл в том, чтобы структура могла следить за своими правилами. Допустим, баланс счёта никогда не должен становиться отрицательным. Если бы любая часть программы могла написать account.balance = ..., любая из них могла бы нарушить это правило, и пришлось бы обыскать весь код, чтобы найти виновника. Если поле приватное, менять его могут только несколько методов внутри mod bank. Проверь правило там — и оно выполняется везде. Эта идея называется инкапсуляцией (encapsulation).

Стандартная библиотека делает так же. У Vec есть приватные поля для длины и буфера памяти. Если бы ты мог написать v.len = 100 для вектора из 3 элементов, чтение v[50] прочитало бы случайную память. Именно приватность len делает Vec безопасным.

Исправление 1: вызови метод вместо чтения поля

Заголовок раздела «Исправление 1: вызови метод вместо чтения поля»

Выбирай этот вариант, когда структура из стандартной библиотеки или другого крейта. Их код ты поменять не можешь, а для всего, что тебе можно видеть, они всегда дают методы. Когда rustc пишет help: a method ... also exists, добавь ():

use std::time::Duration;
fn main() {
let scores = vec![10, 20, 30];
println!("Результатов: {}", scores.len());
let timeout = Duration::from_secs(90);
println!("Тайм-аут: {} с", timeout.as_secs());
}

Вывод:

Результатов: 3
Тайм-аут: 90 с
▶ Попробуй сам

Имя метода не всегда совпадает с именем поля (secs читается через as_secs()). Загляни на страницу типа на docs.rs или дай редактору показать список методов после точки.

Исправление 2: добавь геттер и методы, которые соблюдают правила

Заголовок раздела «Исправление 2: добавь геттер и методы, которые соблюдают правила»

Выбирай этот вариант, когда структура твоя и у поля есть правило, которое надо защитить. Оставь поле приватным, добавь pub fn, который возвращает его значение (геттер, getter), и меняй поле только через методы, которые проверяют правило:

mod bank {
pub struct Account {
pub owner: String,
balance: i64, // приватное: менять может только этот модуль
}
impl Account {
pub fn new(owner: &str) -> Account {
Account { owner: owner.to_string(), balance: 0 }
}
// Геттер: читать баланс может кто угодно.
pub fn balance(&self) -> i64 {
self.balance
}
pub fn deposit(&mut self, amount: i64) {
if amount > 0 {
self.balance += amount;
}
}
// Правило живёт здесь: баланс никогда не уходит ниже нуля.
pub fn withdraw(&mut self, amount: i64) -> Result<(), String> {
if amount > self.balance {
return Err(format!("доступно только {}", self.balance));
}
self.balance -= amount;
Ok(())
}
}
}
fn main() {
let mut account = bank::Account::new("Ferris");
account.deposit(100);
println!("Владелец: {}, на счёте: {}", account.owner, account.balance());
match account.withdraw(500) {
Ok(()) => println!("Сняли 500"),
Err(e) => println!("Не получается снять 500: {e}"),
}
println!("На счёте всё ещё {}", account.balance());
}

Вывод:

Владелец: Ferris, на счёте: 100
Не получается снять 500: доступно только 100
На счёте всё ещё 100
▶ Попробуй сам

Rust разрешает методу и полю иметь одно имя, поэтому геттер обычно называют balance() (а не get_balance()). Когда геттер есть, rustc даже сам его предлагает: help: a method `balance` also exists, call it with parentheses. deposit и withdraw — это твои сеттеры (setters): они меняют поле, но только так, как разрешают правила. Подробнее о методах — в уроке Структуры.

Исправление 3: сделай поле pub, когда данные простые

Заголовок раздела «Исправление 3: сделай поле pub, когда данные простые»

Выбирай этот вариант, когда подходит любое значение и от поля больше ничего не зависит. У точки на сетке, цвета или структуры с настройками нет правил, которые надо защищать. Геттеры там — просто лишний код:

mod geometry {
pub struct Point {
pub x: i32,
pub y: i32,
}
}
fn main() {
let mut p = geometry::Point { x: 3, y: 4 };
p.x += 1;
println!("Точка в ({}, {})", p.x, p.y);
}

Вывод:

Точка в (4, 4)
▶ Попробуй сам

Если поле должен видеть только твой собственный код, пиши pub(crate) x: i32 (любой модуль в этом крейте) или pub(super) x: i32 (только родительский модуль). В программе они ведут себя как pub, а в библиотеке не пускают поле в публичный API. На странице E0603 есть таблица всех уровней видимости.

Может, просто сделать его pub? Спроси себя: «Может ли какое-то значение этого поля или его изменение само по себе сделать структуру неправильной?» Отрицательный баланс, len, который не совпадает с данными, total, который больше не равен сумме товаров, — такие поля оставляй приватными и используй исправление 2. Если ответ «нет, подходит любое значение», pub — честный выбор.

len у Vec. Ты написал scores.len и получил field `len` of struct `Vec` is private. Это потому, что у Vec действительно есть приватное поле с именем len. Исправление: scores.len(). Забавная деталь: name.len у String даёт другую ошибку, E0615 (attempted to take value of method), потому что у String такого поля нет. Исправление то же.

Запись в поле снаружи. account.balance = 500; даёт ту же E0616. Запись тоже запрещена. Не делай поле pub только ради записи — добавь метод вроде deposit, который вносит изменение и проверяет его.

После переноса структуры в отдельный модуль или файл. Код в main.rs месяцами использовал user.email. Потом ты перенёс User в src/user.rs, и все обращения к полям сломались. Поля, с которыми раньше всё было в порядке, теперь приватны для mod user. Добавь pub полям с простыми данными, а для остальных — геттеры.

Тесты. Блок #[cfg(test)] mod tests в том же файле — дочерний модуль, поэтому он может читать приватные поля. Тесты в папке tests/ находятся за пределами твоего крейта и, как любой другой пользователь, могут пользоваться только pub API.

  • E0603: function `x` is private. Та же идея для функций, структур и модулей.
  • E0451: то же приватное поле, но при создании структуры через Account { ... }.
  • E0624: method `x` is private. Методу (или new) тоже нужен pub.
  • E0609: no field `x` on type `T` . Поля вообще не существует (часто это опечатка).
  • E0615: attempted to take value of method. Ты написал x.len, а имел в виду x.len().
  • Все ошибки компилятора: полный список страниц об ошибках.

Проверь себя

1. pub struct Account { balance: u64 } лежит в mod bank. Может ли main прочитать account.balance?

2. Ты получил `field len of struct Vec is private`. Как исправить?

3. У структуры Score есть поле points, которое должно оставаться в пределах от 0 до 100. Какой дизайн лучше?

Результат: 0 / 3