Ошибка Rust E0614: type cannot be dereferenced (лишняя звёздочка *)
E0614 значит: ты поставил * перед тем, что не является ссылкой, поэтому переходить некуда. Оператор * значит «перейди к значению, на которое указывает эта ссылка». А обычный u32, i32 или структура никуда не указывает: это и есть значение. Обычно так бывает в замыканиях и циклах, где легко запутаться, что у тебя — &u32 или u32. rustc называет настоящий тип прямо в заголовке, а исправление почти всегда — удалить одну *. (Эта страница входит в раздел Ошибки компилятора Rust с объяснениями.)
Сообщение об ошибке
Заголовок раздела «Сообщение об ошибке»error[E0614]: type `u32` cannot be dereferenced --> src/main.rs:3:64 |3 | let doubled: Vec<u32> = visits.iter().copied().map(|count| *count * 2).collect(); // ❌ `count` is already a `u32` | ^^^^^^ can't be dereferencedПо-русски: «тип u32 нельзя разыменовать». То есть к значению u32 нельзя применить *.
Вот программа, которая её вызывает (комментарий // ❌ оставлен на английском, потому что rustc цитирует эту строку; он значит «count уже u32»):
fn main() { let visits: Vec<u32> = vec![12, 0, 7, 30]; let doubled: Vec<u32> = visits.iter().copied().map(|count| *count * 2).collect(); // ❌ `count` is already a `u32` println!("{:?}", doubled);}Что это значит
Заголовок раздела «Что это значит»^^^^^^ указывает на *count в строке 3 с пометкой «can’t be dereferenced» («нельзя разыменовать»). Заголовок говорит, чем count является на самом деле: u32, а не &u32.
Пройди по цепочке и проследи типы:
visits.iter()даёт ссылки на элементы:&u32,&u32, ….copied()превращает каждую&u32в обычную копиюu32. Ссылок больше нет.- Поэтому в
.map(|count| ...)count— этоu32, а*countпросит Rust перейти по указателю, которого не существует.
Когда в заголовке написано type `{integer}` cannot be dereferenced, это то же самое. {integer} — так rustc записывает «какой-то целочисленный тип, который я ещё не определил»; обычно это число вроде 72, которому ты не указал тип.
Это как спросить у человека «что лежит по этому адресу?» и вручить ему саму посылку вместо бумажки с адресом. Вещь уже у него в руках — идти некуда.
Почему Rust так делает
Заголовок раздела «Почему Rust так делает»В Rust ссылка (&T) и само значение (T) — разные типы. * работает только с тем, что куда-то указывает: со ссылками &T и &mut T, умными указателями вроде Box<T> и Rc<T> и любым типом, который реализует трейт Deref. У u32 или обычной структуры ничего такого нет, поэтому * к ним не имеет смысла.
Rust мог бы тихо проигнорировать лишнюю *, но не делает этого. Лишняя * обычно значит, что ты ошибаешься насчёт своего типа, а такая путаница часто ведёт к другим багам: сравниваются не те вещи или перемещается значение, которое ты хотел заимствовать. Поэтому rustc останавливается и называет настоящий тип. Подробнее — в уроках Заимствование и Умные указатели.
У обратной ошибки свой код. Если ты забыл * у ссылки, например count > 10, где count — &u32, получишь E0308, несовпадение типов.
Как исправить
Заголовок раздела «Как исправить»Исправление 1: Убери *
Заголовок раздела «Исправление 1: Убери *»Это нужно почти всегда, особенно после .copied(), .cloned(), .into_iter() у Vec или чего-то ещё, что отдаёт значения. Значение уже у тебя, просто используй его:
fn main() { let visits: Vec<u32> = vec![12, 0, 7, 30]; let doubled: Vec<u32> = visits.iter().copied().map(|count| count * 2).collect(); println!("{:?}", doubled);
// `filter` одалживает замыканию каждый элемент, поэтому здесь `count` — это `&u32`, // и `*` стоит правильно. let busy: Vec<u32> = visits.iter().copied().filter(|count| *count > 10).collect(); println!("{:?}", busy);}Вывод:
[24, 0, 14, 60][12, 30]На второй части спотыкаются многие: в одной и той же цепочке map получает сам элемент, а filter — ссылку на элемент. Если сомневаешься, посмотри метод в документации. filter принимает FnMut(&Self::Item) -> bool, с &. Подробнее — в уроках Итераторы и Замыкания.
Исправление 2: В цикле for выбери значения или ссылки
Заголовок раздела «Исправление 2: В цикле for выбери значения или ссылки»Выбирай этот вариант, когда * стоит внутри цикла for. for score in scores разбирает Vec и отдаёт каждое значение, поэтому *score не работает:
error[E0614]: type `{integer}` cannot be dereferenced --> src/main.rs:5:18 |5 | total += *score; // ❌ `score` is a number, not a reference | ^^^^^^ can't be dereferencedПрограмма с ошибкой (комментарий значит «score — число, а не ссылка»):
fn main() { let scores = vec![72, 95, 88]; let mut total = 0; for score in scores { total += *score; // ❌ `score` is a number, not a reference } println!("total: {}", total);}Либо убери *, либо перебирай &scores. Тогда каждый score — это &i32, * стоит правильно, и Vec можно использовать после цикла:
fn main() { let scores = vec![72, 95, 88];
let mut total = 0; for score in &scores { total += *score; // теперь `score` — это `&i32` } println!("итого: {}", total);
// `scores` не переместился в цикл, поэтому он всё ещё здесь. println!("оценок: {}, в среднем {}", scores.len(), total / scores.len() as i32);}Вывод:
итого: 255оценок: 3, в среднем 85Простое правило: for x in v даёт значения, for x in &v — ссылки &, for x in &mut v — ссылки &mut. * нужна только в двух последних случаях. Подробнее — в уроках Циклы for и Векторы.
Исправление 3: Разбери ссылку через &x или используй *x, но не то и другое сразу
Заголовок раздела «Исправление 3: Разбери ссылку через &x или используй *x, но не то и другое сразу»Выбирай этот вариант, когда в образце замыкания или цикла уже есть &, например |&p|. & в образце сам распаковывает ссылку, так что p — обычное число, а *p — уже лишний шаг:
error[E0614]: type `{integer}` cannot be dereferenced --> src/main.rs:3:53 |3 | let with_fee: Vec<i32> = prices.iter().map(|&p| *p + 1).collect(); // ❌ `&p` already removed the `&` | ^^ can't be dereferencedПрограмма с ошибкой (комментарий значит «&p уже убрал &»):
fn main() { let prices = [3, 5, 8]; let with_fee: Vec<i32> = prices.iter().map(|&p| *p + 1).collect(); // ❌ `&p` already removed the `&` println!("{:?}", with_fee);}Выбери один способ избавиться от ссылки. Обе строки ниже делают одно и то же:
fn main() { let prices = [3, 5, 8];
let with_fee: Vec<i32> = prices.iter().map(|&p| p + 1).collect(); // образец убирает `&` let same: Vec<i32> = prices.iter().map(|p| *p + 1).collect(); // `*` убирает `&` println!("{:?} {:?}", with_fee, same);
// `filter` после `.iter()` получает `&&i32`: два `&`, поэтому образец — `&&p`. let pricey = prices.iter().filter(|&&p| p > 4).count(); println!("дорогих товаров: {}", pricey);}Вывод:
[4, 6, 9] [4, 6, 9]дорогих товаров: 2С циклами то же самое: for &(name, qty) in &orders распаковывает каждую ссылку, так что name и qty используй без *. Подробнее — в уроке Сопоставление с образцом.
Исправление 4: Проверь тип и для обёрток используй поле или Deref
Заголовок раздела «Исправление 4: Проверь тип и для обёрток используй поле или Deref»Выбирай этот вариант, когда заголовок называет твой собственный тип, например type `Meters` cannot be dereferenced. Структура — тоже не ссылка, даже если она оборачивает всего одно значение:
error[E0614]: type `Meters` cannot be dereferenced --> src/main.rs:5:18 |5 | let longer = *run + 1.0; // ❌ `run` is a `Meters`, not a reference | ^^^^ can't be dereferencedПрограмма с ошибкой (комментарий значит «run — это Meters, а не ссылка»):
struct Meters(f64);
fn main() { let run = Meters(2.5); let longer = *run + 1.0; // ❌ `run` is a `Meters`, not a reference println!("{} m", longer);}Чтобы достать число из кортежной структуры, используй её поле .0:
struct Meters(f64);
fn main() { let run = Meters(2.5); let longer = run.0 + 1.0; println!("{} м", longer);}Вывод:
3.5 мЕсли твой тип действительно обёртка вроде умного указателя и ты хочешь, чтобы * работала, реализуй для него std::ops::Deref. Делай так только для типов-обёрток; для «нового типа» (newtype) вроде Meters понятнее .0 или метод. Не уверен, какой тип у переменной? Наведи на неё курсор в редакторе (rust-analyzer покажет тип) или доверься заголовку: он всегда называет настоящий тип. Подробнее — в уроках Структуры и Умные указатели.
Где это обычно встречается
Заголовок раздела «Где это обычно встречается»* перед полем структуры. *config.retries, где config — структура (или ссылка на неё), а retries — u32, даёт type `u32` cannot be dereferenced. Точка . уже сама проходит по ссылке. Исправление: config.retries.
Перебор HashMap по значению. for (word, count) in counts { total += *count; } даёт type `u32` cannot be dereferenced, потому что перебор самого counts отдаёт ключи и значения во владение. Исправление: убери * или перебирай &counts, если хеш-таблица нужна и после цикла. Подробнее — в уроке HashMap.
После .copied().max() или .cloned().find(...). Они дают Option<u32>, а не Option<&u32>, поэтому в if let Some(best) = ... { *best } * лишняя. Исправление: используй best напрямую. Без .copied() .iter().max() даёт Option<&u32>, и тогда *best правильно.
Связанные ошибки
Заголовок раздела «Связанные ошибки»- E0308: несовпадение типов; обратная ошибка —
*не хватает там, где ссылка встречается с обычным значением. - E0369: бинарную операцию нельзя применить к типу — ещё одна ошибка, когда ссылки и значения перемешиваются.
- E0507: нельзя переместить значение из-под ссылки; здесь
*работает, но переместила бы значение, которое неCopy. - E0610:
{integer}— примитивный тип без полей, «это просто число»-родственник этой ошибки. - E0599: метод не найден, когда значение не того типа, о котором ты думал.
- Все ошибки компилятора: полный список страниц об ошибках.
Проверь себя
Заголовок раздела «Проверь себя»Проверь себя
1. Почему в visits.iter().copied().map(|count| *count + 1) выражение *count даёт E0614?
2. for score in &scores { total += *score; }, где scores — Vec<i32>. Скомпилируется?
3. prices.iter().map(|&p| *p * 2) даёт E0614. Какая версия правильная?
Результат: 0 / 3
Что почитать
Заголовок раздела «Что почитать»- E0614 — Rust error codes index (на английском): официальное объяснение с минимальным примером и исправлением.
- Ссылки и заимствование — книга «Язык программирования Rust» (перевод на русский): что значат
&и*и почему ссылка и значение — разные типы. - Работа с умными указателями как с обычными ссылками с помощью трейта Deref — книга «Язык программирования Rust» (перевод на русский): как
*проходит по ссылкам и как трейтDerefзаставляет это работать для твоих типов. Iterator::copied— документация стандартной библиотеки (на английском): метод, который превращает элементы&TвT, — самая частая причина лишней*.- Указатели и ссылки — Rust на примерах (перевод на русский): разница между
&valв образце и*valв выражении.