Ошибка Rust E0063: missing field in initializer (пропущенное поле)
E0063 значит: ты создаёшь структуру через Name { ... }, но дал значение не каждому полю. Rust никогда не заполняет поля за тебя, так что каждому нужно значение, пусть даже 0, false или None. Сообщение перечисляет пропущенные поля по именам, так что ты точно знаешь, что добавить. Это одна из самых дружелюбных ошибок в Rust. (Эта страница входит в серию Ошибки компилятора Rust с объяснениями.)
Сообщение об ошибке
Заголовок раздела «Сообщение об ошибке»error[E0063]: missing field `email` in initializer of `User` --> src/main.rs:8:16 |8 | let user = User { | ^^^^ missing `email`Компилятор говорит: при создании User пропущено поле email. Вот программа, которая выдаёт эту ошибку:
struct User { name: String, email: String, active: bool,}
fn main() { let user = User { name: String::from("Ferris"), active: true, // ❌ нет `email` }; println!("{}", user.name);}Что это значит
Заголовок раздела «Что это значит»В заголовке названы пропущенное поле (email) и структура (User). ^^^^ показывает на имя структуры там, где ты начал её создавать, а не на отдельную строку, потому что проблема в том, чего нет.
Если пропущено несколько полей, rustc перечисляет их по алфавиту. Если их два или три, ты увидишь все:
error[E0063]: missing fields `debug`, `retries` and `timeout_secs` in initializer of `Config`Если четыре и больше, он показывает три, а остальные считает:
error[E0063]: missing fields `debug`, `port`, `retries` and 1 other field in initializer of `Config` --> src/main.rs:10:18 |10 | let config = Config { | ^^^^^^ missing `debug`, `port`, `retries` and 1 other field«1 other field» значит, что есть ещё одно поле. Загляни в определение struct Config, чтобы увидеть полный список.
Это как анкета, в которой все графы обязательные. Её не примут с пустой графой «email», даже если ты собирался заполнить её потом.
Почему Rust так делает
Заголовок раздела «Почему Rust так делает»Во многих языках пропущенное поле молча становится null, 0 или пустой строкой. Потом программа работает с пользователем без email и ломается где-то совсем в другом месте, часто уже в продакшене. В Rust нет null, и он не придумывает значения у тебя за спиной. У User всегда есть настоящий email. Смотри Объявляем и создаём структуру.
Это не значит, что каждый раз нужно печатать все поля. В Rust есть короткие способы сказать «возьми значения по умолчанию» или «скопируй остальное из другого значения» — это показывают исправления ниже. Главное, что выбираешь ты, и этот выбор записан в коде.
Как исправить
Заголовок раздела «Как исправить»Исправление 1: добавь пропущенное поле
Заголовок раздела «Исправление 1: добавь пропущенное поле»Выбирай этот вариант в большинстве случаев. Поле существует не просто так, поэтому дай ему настоящее значение. Если у тебя есть переменная с тем же именем, что и поле, можно использовать сокращённую запись User { name, email, active }.
struct User { name: String, email: String, active: bool,}
fn main() { let user = User { name: String::from("Ferris"), active: true, }; println!("{} <{}> активен: {}", user.name, user.email, user.active);}Вывод:
Ferris <[email protected]> активен: trueИсправление 2: выведи Default и используй ..Default::default()
Заголовок раздела «Исправление 2: выведи Default и используй ..Default::default()»Выбирай этот вариант, когда у структуры много полей и у большинства есть очевидное «пустое» значение, как у настроек или опций. Добавь структуре #[derive(Default)]. Потом пиши только нужные поля и заканчивай ..Default::default(). .. значит «возьми все поля, которые я не написал, из этого значения».
#[derive(Debug, Default)]struct Config { host: String, port: u16, debug: bool, timeout_secs: u64, retries: u8,}
fn main() { let config = Config { host: String::from("localhost"), port: 8080, ..Default::default() // ✅ debug, timeout_secs и retries получают значения по умолчанию }; println!("{:?}", config);}Вывод:
Config { host: "localhost", port: 8080, debug: false, timeout_secs: 0, retries: 0 }Выведенные значения по умолчанию: 0 для чисел, false для bool, пустая String и None для Option. Если 0 — плохое значение по умолчанию (тайм-аут в 0 секунд, скорее всего, плохой), напиши impl Default for Config сам, с более удачными значениями. Смотри Default: разумное начальное значение.
Если забудешь #[derive(Default)], ошибка сменится на E0277: the trait bound `Config: Default` is not satisfied, с подсказкой help: consider annotating `Config` with `#[derive(Default)]`.
Исправление 3: напиши конструктор new
Заголовок раздела «Исправление 3: напиши конструктор new»Выбирай этот вариант, когда создаёшь структуру во многих местах или когда некоторые поля всегда начинаются одинаково. Помести полный литерал структуры в одну функцию и вызывай её везде. Когда потом добавишь поле, обновишь только new, а не каждое место вызова.
struct Player { name: String, score: u32, level: u8,}
impl Player { fn new(name: &str) -> Player { Player { name: name.to_string(), score: 0, // каждый новый игрок начинает с 0... level: 1, // ...на первом уровне } }}
fn main() { let ana = Player::new("Аня"); let mut bo = Player::new("Боря"); bo.score += 50; println!("{}: очки {}, уровень {}", ana.name, ana.score, ana.level); println!("{}: очки {}, уровень {}", bo.name, bo.score, bo.level);}Вывод:
Аня: очки 0, уровень 1Боря: очки 50, уровень 1Литералу структуры внутри new всё равно нужны все поля, так что забытое поле покажется там как E0063. И это хорошо: исправляешь один раз. Смотри Функции-конструкторы.
Исправление 4: скопируй остальное из другого значения через ..other
Заголовок раздела «Исправление 4: скопируй остальное из другого значения через ..other»Выбирай этот вариант, когда у тебя уже есть похожее значение и нужна копия с парой изменений. ..base берёт из base все поля, которые ты не написал:
#[derive(Debug, Clone)]struct Theme { name: String, font_size: u8, dark: bool,}
fn main() { let base = Theme { name: String::from("Обычная"), font_size: 14, dark: false };
// меняются только `name` и `dark`, остальное берётся из `base` let night = Theme { name: String::from("Ночная"), dark: true, ..base };
// `..base` переместил бы `name` из base, поэтому клонируем, чтобы base остался доступен let large = Theme { font_size: 20, ..base.clone() };
println!("{:?}", night); println!("{:?}", large); println!("{:?}", base);}Вывод:
Theme { name: "Ночная", font_size: 14, dark: true }Theme { name: "Обычная", font_size: 20, dark: false }Theme { name: "Обычная", font_size: 14, dark: false }Одна вещь, которую стоит знать: ..base перемещает поля вроде String из base. В night это не страшно: мы написали свой name, так что из base пришли только Copy-поля (font_size). В large поле name пришло бы из base, поэтому мы пишем ..base.clone(), чтобы base оставался доступен. Если пропустить клонирование и потом использовать base, получишь E0382.
Когда помогают поля Option
Заголовок раздела «Когда помогают поля Option»Иногда поле действительно может быть пустым: телефон, который пользователь ещё не указал, прозвище, дата окончания. Сделай такое поле Option. Написать его всё равно придётся (Rust не подставит None за тебя), но теперь None — честное, разрешённое значение. Смотри урок Option.
#[derive(Debug, Default)]struct Contact { name: String, email: String, phone: Option<String>, // может отсутствовать nickname: Option<String>,}
fn main() { let a = Contact { name: String::from("Аня"), phone: None, // написано явно: «телефона пока нет» nickname: Some(String::from("Анечка")), }; // с Default каждый пропущенный Option становится None let b = Contact { name: String::from("Боря"), ..Default::default() };
println!("{}, телефон: {:?}, прозвище: {:?}", a.name, a.phone, a.nickname); println!("{}, email: {:?}, телефон: {:?}", b.name, b.email, b.phone);}Вывод:
Аня, телефон: None, прозвище: Some("Анечка")Боря, email: "", телефон: NoneОбрати внимание: b.email — это "". В этом ловушка Default: обязательное поле молча становится пустым. Используй Option только для того, что действительно необязательно, а обязательные поля вроде email оставляй обычными типами, которые ты всегда заполняешь.
Где это обычно встречается
Заголовок раздела «Где это обычно встречается»Сразу после того, как ты добавил поле в структуру. Ты добавляешь created_at в User, и вдруг каждый User { ... } в проекте даёт E0063. Это Rust тебе помогает: каждое такое место — то, о котором нужно подумать. Конструктор new (исправление 3) делает этот список коротким.
Варианты перечисления с именованными полями. Shape::Rectangle { width: 2.0 } даёт missing field `height` in initializer of `Shape`. Обрати внимание, что в сообщении названо перечисление, а не вариант. Исправление: дай значение каждому полю этого варианта. Смотри Перечисления могут хранить данные.
Функция, которая возвращает структуру. В fn new_player(name: &str) -> Player { Player { name: name.to_string(), score: 0 } } стрелка показывает на Player внутри функции, с missing `level`. Исправление: добавь level в этот литерал.
Связанные ошибки
Заголовок раздела «Связанные ошибки»- E0560: struct has no field named x: обратная ситуация — ты написал поле, которого у структуры нет. Опечатка часто даёт обе ошибки сразу.
- E0559: variant has no field named x: та же ошибка с неверным именем, но для вариантов перечисления.
- E0277: trait bound not satisfied: ты написал
..Default::default(), но структура не выводитDefault. - E0451: field is private: структура из другого модуля, и часть её полей приватна, поэтому заполнить их нельзя. Если пропустить приватное поле, будет не E0063, а «cannot construct
Accountwith struct literal syntax due to private fields». В любом случае используй функциюnewэтого модуля. - E0382: use of moved value: ты использовал значение после того, как
..valueпереместил из него поля. - Все ошибки компилятора с объяснениями
Проверь себя
Заголовок раздела «Проверь себя»Проверь себя
1. rustc пишет: missing fields a, b, c and 2 other fields. Сколько полей пропущено?
2. У твоей структуры есть поле Option<String>, которое пока не нужно. Что для него написать?
3. Что нужно, чтобы Config { port: 80, ..Default::default() } скомпилировалось?
Результат: 0 / 3
Что почитать
Заголовок раздела «Что почитать»- E0063 — Rust error codes index (на английском): официальное объяснение этой ошибки.
- Определение и создание экземпляров структур — книга «Язык программирования Rust» (перевод на русский): литералы структур, сокращённая инициализация полей и синтаксис обновления структуры.
Default— стандартная библиотека Rust (на английском):#[derive(Default)], собственная реализацияimpl Defaultи..Default::default().- Struct expressions — The Rust Reference (на английском): точные правила для
Name { field: value }и..base. - Constructors — Rust API Guidelines (на английском): почему в Rust-коде структуры создают через функции
new.