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

Ошибка 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», даже если ты собирался заполнить её потом.

Во многих языках пропущенное поле молча становится null, 0 или пустой строкой. Потом программа работает с пользователем без email и ломается где-то совсем в другом месте, часто уже в продакшене. В Rust нет null, и он не придумывает значения у тебя за спиной. У User всегда есть настоящий email. Смотри Объявляем и создаём структуру.

Это не значит, что каждый раз нужно печатать все поля. В Rust есть короткие способы сказать «возьми значения по умолчанию» или «скопируй остальное из другого значения» — это показывают исправления ниже. Главное, что выбираешь ты, и этот выбор записан в коде.

Выбирай этот вариант в большинстве случаев. Поле существует не просто так, поэтому дай ему настоящее значение. Если у тебя есть переменная с тем же именем, что и поле, можно использовать сокращённую запись User { name, email, active }.

struct User {
name: String,
email: String,
active: bool,
}
fn main() {
let user = User {
name: String::from("Ferris"),
email: String::from("[email protected]"), // ✅ добавили
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)]`.

Выбирай этот вариант, когда создаёшь структуру во многих местах или когда некоторые поля всегда начинаются одинаково. Помести полный литерал структуры в одну функцию и вызывай её везде. Когда потом добавишь поле, обновишь только 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. Написать его всё равно придётся (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("Аня"),
email: String::from("[email protected]"),
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 Account with 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