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

Ошибка Rust E0610: primitive type doesn't have fields (поле у числа)

E0610 значит: ты написал something.field, но something — обычное число (или bool, или char), а у них вообще нет полей. Почти всегда это значит, что переменная — не то, чем ты её считаешь. Может быть, ты уже достал число из структуры, функция вернула число вместо структуры, или ты написал на один .0 больше, чем нужно. Сообщение называет точный тип, который видит Rust, так что до исправления один шаг. (Эта страница входит в серию Ошибки компилятора Rust с объяснениями.)

error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields
--> src/main.rs:3:22
|
3 | println!("{}", x.value); // ❌ x is a number, it has no `value` field
| ^^^^^

Компилятор говорит: {integer} — примитивный тип, и поэтому у него нет полей. Вот программа, которая выдаёт эту ошибку (комментарий значит «x — число, у него нет поля value»):

fn main() {
let x = 5;
println!("{}", x.value); // ❌ x is a number, it has no `value` field
}

Тип в обратных кавычках — это то, чем Rust считает твою переменную. Здесь это `{integer}`, то есть «какое-то целое число, какое именно — я ещё не решил» (если ничто другое не решит, Rust потом выберет i32). Ещё встречается `{float}` для дробных чисел или конкретный тип: u32, usize, f64, bool или char.

^^^^^ показывает на имя после точки. Rust не говорит «value написано с ошибкой». Он говорит «у числа не может быть никакого поля, как его ни назови».

Поэтому задай себе вопрос: почему эта переменная — число? Это как просить «ценник на пятёрке». У товара есть ценник. А число 5 — это просто число.

Примитивные типы вроде i32, u32, f64, bool и char — это отдельные значения, встроенные в язык. Они не структуры, поэтому внутри них нет ничего, до чего можно добраться через точку и имя. Подробнее — в уроке Типы данных.

Зато у них есть методы: x.abs(), x.pow(2), price.round(), c.is_alphabetic(). Методы вызываются с (). Поля — это данные внутри структуры, и они есть только у структур, кортежей и объединений (union).

Rust проверяет каждое .field во время компиляции, поэтому такая путаница никогда не превратится в падение программы или тихий undefined во время выполнения. Ты узнаешь о ней сразу и ровно на нужной строке.

Выбирай этот вариант, когда ты уже достал поле из структуры раньше. После let count = counter.value; переменная count и есть число, так что count.value просит поле у u32:

error[E0610]: `u32` is a primitive type and therefore doesn't have fields
--> src/main.rs:8:26
|
8 | println!("{}", count.value);
| ^^^^^

Либо используй число как есть, либо вернись к структуре и прочитай поле оттуда:

struct Counter {
value: u32,
}
fn main() {
let mut counter = Counter { value: 0 };
counter.value += 3;
let count = counter.value; // теперь count — обычный u32
println!("{}", count); // ✅ используем само число
println!("{}", counter.value); // ✅ или читаем поле из структуры
}

Вывод:

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

Не уверен, какой тип у переменной? Наведи на неё курсор в редакторе (с rust-analyzer, смотри Инструменты Rust). Или ненадолго напиши let _: () = count;: rustc ответит expected `()`, found `usize` и назовёт настоящий тип. Потом удали эту строку.

Исправление 2: возвращай или храни структуру, а не число

Заголовок раздела «Исправление 2: возвращай или храни структуру, а не число»

Выбирай этот вариант, когда значение пришло из функции или коллекции, а ты ждал структуру. Два частых случая:

  • Функция вроде fn find_order() -> u32 возвращает номер заказа, и тогда order.total не работает, потому что order — это u32.
  • HashMap<&str, u32> хранит только очки, и тогда scores["ana"].score не работает, потому что каждое значение — u32, а не Player.

Реши, что тебе на самом деле нужно. Если нужно несколько кусочков данных, поменяй тип возвращаемого значения или тип значений в коллекции на структуру:

use std::collections::HashMap;
struct Order {
id: u32,
total: u32, // в центах
}
// возвращает весь Order, а не просто число
fn new_order(id: u32, total: u32) -> Order {
Order { id, total }
}
struct Player {
score: u32,
level: u8,
}
fn main() {
let order = new_order(7, 1999);
println!("Заказ {} стоит {} центов", order.id, order.total);
// в таблице хранятся целые Player, а не только их очки
let mut players: HashMap<&str, Player> = HashMap::new();
players.insert("ana", Player { score: 10, level: 2 });
let ana = &players["ana"];
println!("ana: очки {}, уровень {}", ana.score, ana.level);
}

Вывод:

Заказ 7 стоит 1999 центов
ana: очки 10, уровень 2
▶ Попробуй сам

Если тебе нужно только число, оставь u32 и просто убери часть .field. Смотри уроки Структуры и HashMap.

Выбирай этот вариант, когда ошибка показывает на .0 или .1. У кортежей и кортежных структур поля обозначаются позициями, и легко зайти на один уровень глубже, чем нужно. После let x = point.0; переменная x — уже число, так что x.0 даёт `{integer}` is a primitive type. То же самое с обёрткой вроде struct Meters(u32);: первый .0 достаёт u32, а второй .0 уже не работает.

struct Meters(u32);
fn main() {
let point = (3, 4);
let x = point.0; // x — уже число 3
println!("x = {}", x); // не x.0
let m = Meters(42);
println!("{} м", m.0); // один .0 достаёт u32 из кортежной структуры
let line = ((0, 0), (3, 4)); // вложенные кортежи: одна позиция на уровень
println!("y конца = {}", line.1.1);
}

Вывод:

x = 3
42 м
y конца = 4
▶ Попробуй сам

Считай уровни: каждый .0 или .1 заходит на один уровень внутрь кортежа. Дошёл до числа — остановись. Если позиции начинают путать, дай им имена с помощью структуры. Смотри урок Кортежи: там есть доступ к элементам по позиции и совет, когда выбирать кортеж, а когда структуру.

Метод без (). price.round у f64 или x.abs у i32 — это не E0610, а E0615, «attempted to take value of method», потому что эти имена и есть методы. Исправление: price.round(), x.abs(). Один сюрприз: если тип всё ещё {integer} (как в let x = -5;), даже x.abs даёт E0610, а x.abs() потом даёт E0689 «can’t call method abs on ambiguous numeric type {integer}». Выбери тип, let x: i32 = -5;, и x.abs() заработает.

Ссылка на число. Если переменная — &u32, а не u32, код ошибки меняется на E0609: no field `value` on type `&u32`. Частое место — цикл вроде for (name, score) in &players, где score — это &i32, и score.1 даёт no field `1` on type `&{integer}`. Причина та же, исправления те же: score — уже число.

Результат len() или count(). let len = scores.len(); даёт usize, так что len.count падает с `usize` is a primitive type. Исправление: используй len напрямую.

Числа с единицей измерения. Если хочется писать price.cents или distance.meters, сделай маленькую структуру: struct Price { cents: u64 }. Тогда price.cents работает, а тип не даст перепутать цены с другими числами. Смотри урок Структуры.

Проверь себя

1. Что даст total.amount после let total = cart.total; (где total — поле типа u32)?

2. Что значит {integer} в сообщении?

3. let x = point.0;, а потом x.0 даёт E0610. Как исправить?

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