Ошибка 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 так делает
Заголовок раздела «Почему Rust так делает»В 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На счёте всё ещё 100Rust разрешает методу и полю иметь одно имя, поэтому геттер обычно называют 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
Что почитать
Заголовок раздела «Что почитать»- E0616 — Rust error codes index (на английском): официальное объяснение с минимальным примером.
- Делаем общедоступными структуры и перечисления — книга «Язык программирования Rust» (перевод на русский): почему у
pub structвсё равно приватные поля, на примере ресторана. - Инкапсуляция, скрывающая детали реализации — книга «Язык программирования Rust» (перевод на русский): структура, которая поддерживает своё среднее значение верным, потому что её поля приватны.
- Видимость структуры — Rust на примерах (перевод на русский): публичные и приватные поля рядом, в одном запускаемом примере.
- Structs have private fields (C-STRUCT-PRIVATE) — Rust API Guidelines (на английском): почему библиотеки держат поля приватными, чтобы потом их можно было менять.