Ошибка Rust E0609: no field X on type T (у типа нет такого поля)
E0609 значит: ты написал value.something, но у типа value нет поля something. Чаще всего это опечатка или значение оказалось не совсем того типа, который ты думаешь (например, Option<User> вместо User). Сообщение об ошибке показывает точный тип, который видит Rust, и обычно этого достаточно для исправления. (Эта страница — часть раздела Ошибки компилятора Rust с объяснениями.)
Сообщение об ошибке
Заголовок раздела «Сообщение об ошибке»error[E0609]: no field `emial` on type `User` --> src/main.rs:12:25 |12 | println!("{}", user.emial); // ❌ typo: the field is `email` | ^^^^^ unknown field |help: a field with a similar name exists |12 - println!("{}", user.emial); // ❌ typo: the field is `email`12 + println!("{}", user.email); // ❌ typo: the field is `email` |Компилятор пишет: у типа User нет поля emial, — и подсказывает, что есть поле с похожим именем. Вот программа, которая выдаёт эту ошибку:
struct User { name: String, email: String,}
fn main() { let user = User { name: String::from("Ferris"), }; println!("{}", user.name); println!("{}", user.emial); // ❌ typo: the field is `email`}Комментарий говорит: опечатка, поле называется email.
Что это значит
Заголовок раздела «Что это значит»В заголовке две части, и обе важны:
no field `emial`: имя после точки, которое Rust не нашёл.on type `User`: тип, который, по мнению Rust, уuser.
Так что сначала проверь имя, а потом тип. Если тип в сообщении — не та структура, которую ты ожидал, например Option<User>, &Option<User>, Vec<User> или &T, то с именем всё в порядке, а проблема в том, что сначала нужно достать структуру.
Это как спросить в магазине «синюю, на второй полке», стоя не в том магазине. Просьба нормальная, просто ты не там.
Почему Rust так делает
Заголовок раздела «Почему Rust так делает»В Rust поля каждой структуры фиксируются, когда ты её пишешь. Компилятор знает полный список и проверяет каждое .поле во время компиляции. Нет никакого «поищем во время работы и получим undefined», как в JavaScript, или AttributeError, как в Python: неверное имя поля никогда не попадёт в твою программу. Подробнее в уроке Структуры.
Типы-обёртки вроде Option<T> и Result<T, E> — это отдельные типы: твоя структура лежит внутри них, но её полей у них нет. Rust не тянется сквозь них за тебя, потому что значения может вообще не быть. Ты должен сказать, что делать, если его нет. Подробнее в уроке Option.
Обобщённый код подчиняется тому же строгому правилу. В fn greet<T>(x: &T) T может быть любым типом, включая i32 или bool, у которых полей нет. Rust проверяет обобщённую функцию один раз сразу для всех возможных T, поэтому разрешает только то, что обещают твои ограничения трейтов. А трейты могут обещать только методы, но не поля. Подробнее в уроках Обобщения и Трейты.
Как исправить
Заголовок раздела «Как исправить»Исправление 1: исправь написание
Заголовок раздела «Исправление 1: исправь написание»Выбирай это, когда тип в сообщении — та структура, которую ты ожидал. Найди строку help: a field with a similar name exists; обычно rustc точно знает, какое поле ты имел в виду:
struct User { name: String, email: String,}
fn main() { let user = User { name: String::from("Ferris"), }; println!("{}", user.name); println!("{}", user.email); // ✅ настоящее имя поля}Вывод:
FerrisЕсли подсказки нет, перейди к определению struct (в большинстве редакторов — Ctrl+клик или F12 по типу) и скопируй имя оттуда. Регистр в именах полей важен: userName и user_name — разные поля.
Исправление 2: сначала достань структуру из Option
Заголовок раздела «Исправление 2: сначала достань структуру из Option»Выбирай это, когда сообщение говорит, что тип — Option<User>, Option<&User> или &Option<User>. Так постоянно бывает с функциями, которые могут ничего не найти, и с методами вроде .first(), .last(), .get() и .find():
error[E0609]: no field `name` on type `Option<User>` --> src/main.rs:11:25 |11 | println!("{}", user.name); | ^^^^ unknown field |help: one of the expressions' fields has a field of the same name |11 | println!("{}", user.unwrap().name); | +++++++++Компилятор говорит, что у Option<User> нет поля name, но у значения внутри такое поле есть. Подсказка с .unwrap() скомпилируется, но программа упадёт, если значение окажется None. Лучше решить, что должно происходить в обоих случаях, — через if let, match или map со значением по умолчанию:
struct User { name: String,}
fn find_user(id: u32) -> Option<User> { if id == 1 { Some(User { name: String::from("Ferris") }) } else { None }}
fn main() { // if let: код выполняется, только если User есть if let Some(user) = find_user(1) { println!("Найден: {}", user.name); }
// match: обрабатываем оба случая match find_user(2) { Some(user) => println!("Найден: {}", user.name), None => println!("Нет пользователя с id 2"), }
// map + unwrap_or: берём поле или значение по умолчанию let users = vec![User { name: String::from("Ada") }]; let first_name = users.first().map(|u| u.name.as_str()).unwrap_or("никто"); println!("Первый пользователь: {first_name}");}Вывод:
Найден: FerrisНет пользователя с id 2Первый пользователь: AdaТе же приёмы работают и со ссылкой на Option. Если maybe_user — это &Option<User>, пиши if let Some(user) = maybe_user, и user будет &User. (Для &Option<User> rustc вообще не даёт подсказки, так что это стоит запомнить.) Внутри функции, которая возвращает Option, работает и let user = find_user(1)?;. Подробнее в уроках Option и if let.
Исправление 3: используй .0 и .1 у кортежных структур
Заголовок раздела «Исправление 3: используй .0 и .1 у кортежных структур»Выбирай это, когда тип — кортежная структура, например struct Point(i32, i32);. У её полей нет имён, только позиции, поэтому p.x даёт no field `x` on type `Point` (с подсказкой p.0). Используй позицию или разбери структуру в именованные переменные. Если нужны имена, сделай обычную структуру с именованными полями:
struct Point(i32, i32); // кортежная структура: поля .0 и .1
struct Size { width: u32, // именованные поля: используй .width и .height height: u32,}
fn main() { let p = Point(3, 4); println!("x = {}, y = {}", p.0, p.1);
let Point(x, y) = p; // или разбери её в именованные переменные println!("квадрат расстояния = {}", x * x + y * y);
let screen = Size { width: 1920, height: 1080 }; println!("{}x{}", screen.width, screen.height);}Вывод:
x = 3, y = 4квадрат расстояния = 251920x1080Обычные кортежи работают так же. pair.2 у кортежа из двух элементов даёт E0609 с примечанием available fields are: `0`, `1` (доступные поля: 0, 1). Подробнее в уроке Кортежи.
Исправление 4: метод трейта вместо поля у обобщённого T
Заголовок раздела «Исправление 4: метод трейта вместо поля у обобщённого T»Выбирай это, когда сообщение говорит on type `&T` (или T) с подписью type parameter 'T' declared here. Такой код не компилируется, даже если у каждого типа, который ты передаёшь, есть поле name:
fn greet<T>(animal: &T) { println!("Hello, {}!", animal.name); // ❌ no field `name` on type `&T`}Трейты не могут требовать полей, но могут требовать метод-геттер. Объяви маленький трейт, реализуй его для каждого типа и добавь его ограничением на T:
trait Named { fn name(&self) -> &str;}
struct Dog { name: String,}
struct Robot { model: String,}
impl Named for Dog { fn name(&self) -> &str { &self.name }}
impl Named for Robot { fn name(&self) -> &str { &self.model // поле даже не обязано называться так же }}
fn greet<T: Named>(thing: &T) { println!("Привет, {}!", thing.name()); // вызов метода, а не поле}
fn main() { greet(&Dog { name: String::from("Rex") }); greet(&Robot { model: String::from("R2-D2") });}Вывод:
Привет, Rex!Привет, R2-D2!Если функция всегда работает только с одним типом, обобщение вообще не нужно: напиши fn greet(animal: &Dog), и animal.name заработает. Подробнее в уроке Трейты.
Где это обычно встречается
Заголовок раздела «Где это обычно встречается»Метод без (). word.len или cart.total (где total — метод) близки к E0609, но rustc выдаёт другой код: E0615, «attempted to take value of method», с подсказкой «use parentheses to call the method» (добавь скобки, чтобы вызвать метод). У Vec строка scores.len даёт E0616, «field len of struct Vec is private», потому что у Vec действительно есть скрытое поле с таким именем. Исправление для обоих одно: word.len(), scores.len(), cart.total(). А "hello".length — это обычная E0609 для &str: в Rust этот метод называется len(). Подробнее в уроке Строки.
Vec вместо одного элемента. dogs.name для Vec<Dog> даёт no field `name` on type `Vec<Dog>`. У списка нет name, оно есть у каждого элемента. Исправление: выбери один через dogs[0].name или пройди циклом: for dog in &dogs { println!("{}", dog.name); }. Подробнее в уроке Векторы.
Result вместо значения. load_config().port даёт no field `port` on type `Result<Config, String>` с той же подсказкой .unwrap(). Исправление: let config = load_config()?; внутри функции, которая возвращает Result, или match. Подробнее об операторе ? — в уроке Result.
Поле у числа. age.value для u32 — это снова другой код: E0610, «u32 is a primitive type and therefore doesn’t have fields» (u32 — примитивный тип, полей у него нет). У чисел, bool и char полей нет вообще; скорее всего, ты имел в виду переменную-структуру.
Связанные ошибки
Заголовок раздела «Связанные ошибки»- E0599: no method named x found: версия этой ошибки для методов. Причины часто те же: опечатка или
Option, из которого нужно достать значение. - E0425: cannot find value in this scope: переменной перед точкой вообще не существует, или ты забыл
self.перед полем. - E0308: mismatched types: то, что часто появляется следом, если достать значение из
Optionв одном месте и забыть в другом. E0615: attempted to take value of method: ты написалx.lenвместоx.len().E0616: field is private: поле существует, но неpub, какlenуVec. Пользуйся методами типа.E0560: struct has no field named x: та же опечатка, но при создании структуры (User { emial: ... }).E0610: primitive type doesn't have fields:.somethingу числа,boolилиchar.- Все ошибки компилятора с объяснениями
Проверь себя
Заголовок раздела «Проверь себя»Проверь себя
1. Ошибка говорит, что у типа Option<User> нет поля name. О чём это говорит?
2. Как прочитать первое поле struct Meters(f64); у значения m?
3. Как в fn show<T>(x: &T) прочитать имя у любого T?
Результат: 0 / 3
Что почитать
Заголовок раздела «Что почитать»- E0609 — список кодов ошибок Rust (на английском): официальное объяснение этой ошибки.
- Определение и создание экземпляров структур — книга «Язык программирования Rust» (перевод на русский): именованные поля, доступ через точку и синтаксис обновления структуры.
- Кортежные структуры — книга «Язык программирования Rust» (перевод на русский): структуры, к полям которых обращаются по позиции, например
.0. - Перечисление Option — книга «Язык программирования Rust» (перевод на русский): почему
Option<T>иT— разные типы и почему значение нужно доставать. - Трейты: определение общего поведения — книга «Язык программирования Rust» (перевод на русский): методы трейтов и ограничения трейтов — правильный инструмент для обобщённого кода.