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

Ошибка 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"),
email: String::from("[email protected]"),
};
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 поля каждой структуры фиксируются, когда ты её пишешь. Компилятор знает полный список и проверяет каждое .поле во время компиляции. Нет никакого «поищем во время работы и получим undefined», как в JavaScript, или AttributeError, как в Python: неверное имя поля никогда не попадёт в твою программу. Подробнее в уроке Структуры.

Типы-обёртки вроде Option<T> и Result<T, E> — это отдельные типы: твоя структура лежит внутри них, но её полей у них нет. Rust не тянется сквозь них за тебя, потому что значения может вообще не быть. Ты должен сказать, что делать, если его нет. Подробнее в уроке Option.

Обобщённый код подчиняется тому же строгому правилу. В fn greet<T>(x: &T) T может быть любым типом, включая i32 или bool, у которых полей нет. Rust проверяет обобщённую функцию один раз сразу для всех возможных T, поэтому разрешает только то, что обещают твои ограничения трейтов. А трейты могут обещать только методы, но не поля. Подробнее в уроках Обобщения и Трейты.

Выбирай это, когда тип в сообщении — та структура, которую ты ожидал. Найди строку help: a field with a similar name exists; обычно rustc точно знает, какое поле ты имел в виду:

struct User {
name: String,
email: String,
}
fn main() {
let user = User {
name: String::from("Ferris"),
email: String::from("[email protected]"),
};
println!("{}", user.name);
println!("{}", user.email); // ✅ настоящее имя поля
}

Вывод:

▶ Попробуй сам

Если подсказки нет, перейди к определению 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
квадрат расстояния = 25
1920x1080
▶ Попробуй сам

Обычные кортежи работают так же. 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 полей нет вообще; скорее всего, ты имел в виду переменную-структуру.

Проверь себя

1. Ошибка говорит, что у типа Option<User> нет поля name. О чём это говорит?

2. Как прочитать первое поле struct Meters(f64); у значения m?

3. Как в fn show<T>(x: &T) прочитать имя у любого T?

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