Ошибка 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 — это просто число.
Почему Rust так делает
Заголовок раздела «Почему Rust так делает»Примитивные типы вроде i32, u32, f64, bool и char — это отдельные значения, встроенные в язык. Они не структуры, поэтому внутри них нет ничего, до чего можно добраться через точку и имя. Подробнее — в уроке Типы данных.
Зато у них есть методы: x.abs(), x.pow(2), price.round(), c.is_alphabetic(). Методы вызываются с (). Поля — это данные внутри структуры, и они есть только у структур, кортежей и объединений (union).
Rust проверяет каждое .field во время компиляции, поэтому такая путаница никогда не превратится в падение программы или тихий undefined во время выполнения. Ты узнаешь о ней сразу и ровно на нужной строке.
Как исправить
Заголовок раздела «Как исправить»Исправление 1: используй число напрямую
Заголовок раздела «Исправление 1: используй число напрямую»Выбирай этот вариант, когда ты уже достал поле из структуры раньше. После 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); // ✅ или читаем поле из структуры}Вывод:
33Не уверен, какой тип у переменной? Наведи на неё курсор в редакторе (с 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.
Исправление 3: убери лишний .0
Заголовок раздела «Исправление 3: убери лишний .0»Выбирай этот вариант, когда ошибка показывает на .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 = 342 м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 работает, а тип не даст перепутать цены с другими числами. Смотри урок Структуры.
Связанные ошибки
Заголовок раздела «Связанные ошибки»- E0609: no field
xon typeT: у типа нет поля с таким именем. Для ссылок вроде&u32ты получишь именно её (а не E0610). - E0615: attempted to take value of method: ты написал
price.roundвместоprice.round(). E0689: can't call method on ambiguous numeric type: метод у числа, тип которого ещё не выбран. Добавь тип, напримерi32.- E0599: no method named x found: то же для методов, например метод, которого у чисел нет.
- E0308: mismatched types: часто появляется рядом с E0610, когда функция возвращает число, а ты ждал структуру.
- Все ошибки компилятора с объяснениями
Проверь себя
Заголовок раздела «Проверь себя»Проверь себя
1. Что даст total.amount после let total = cart.total; (где total — поле типа u32)?
2. Что значит {integer} в сообщении?
3. let x = point.0;, а потом x.0 даёт E0610. Как исправить?
Результат: 0 / 3
Что почитать
Заголовок раздела «Что почитать»- E0610 — Rust error codes index (на английском): официальное объяснение этой ошибки.
- Типы данных — книга «Язык программирования Rust» (перевод на русский): скалярные типы (целые, дробные,
bool,char) и составные (кортежи, массивы). - Кортежные структуры — книга «Язык программирования Rust» (перевод на русский): типы-обёртки вроде
Meters(u32)и как с ними работает.0. - Примитивы — Rust на примерах (перевод на русский): встроенные типы с запускаемыми примерами.
i32— стандартная библиотека Rust (на английском): все методы целого числа (и видно, что все они вызываются с()).