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

Ошибка Rust E0308: mismatched types (несовпадение типов)

Ошибка E0308 значит, что Rust ожидал значение одного типа, а ты дал ему значение другого типа. Скорее всего, это ошибка, которую ты будешь видеть в Rust чаще всех, но в сообщении почти всегда указаны оба типа, так что исправляется она обычно быстро. (Ищешь другую ошибку? Загляни в обзор ошибок компилятора.)

error[E0308]: mismatched types
--> src/main.rs:6:29
|
6 | let user = User { name: "Ferris" };
| ^^^^^^^^ expected `String`, found `&str`
|
help: try using a conversion method
|
6 | let user = User { name: "Ferris".to_string() };
| ++++++++++++

«Mismatched types» значит «несовпадение типов»: ожидался String, а найден &str. Подсказка help предлагает метод преобразования.

Вот программа, которая выдаёт эту ошибку:

struct User {
name: String,
}
fn main() {
let user = User { name: "Ferris" };
println!("{}", user.name);
}

У каждого сообщения E0308 одна и та же форма: expected A, found B («ожидался A, найден B»).

  • Expected — тип, который Rust нужен в этом месте. Он берётся откуда-то ещё: из аннотации типа, параметра или возвращаемого типа функции, поля структуры или другой ветки if/match.
  • Found — тип значения, которое ты там на самом деле написал.

В примере поле name объявлено как String, поэтому Rust ожидает именно его. Но "Ferris" — строковый литерал, у него тип &str. Эти типы связаны, но это не один и тот же тип.

Смотри и на строки help:. Для E0308 компилятор часто предлагает точное исправление, как .to_string() выше.

Rust никогда не преобразует типы молча. В некоторых языках число тихо превращается в строку, а большое число тихо обрезается, чтобы влезть в меньший тип. Такие скрытые преобразования вызывают настоящие баги: потерю данных, неверные результаты, падения далеко от места настоящей ошибки.

Поэтому Rust делает каждое преобразование видимым в коде. Печатать приходится чуть больше, зато, читая код потом, ты точно видишь, где значение меняет тип и может ли этот шаг не сработать. Сами типы описаны в уроке Типы данных.

Каждое исправление ниже начинается с формы ошибки, так что можно сразу перейти к тому, что похоже на твой случай.

Исправление 1: убери точку с запятой в конце

Заголовок раздела «Исправление 1: убери точку с запятой в конце»
error[E0308]: mismatched types
--> src/main.rs:1:46
|
1 | fn total_price(price: u32, quantity: u32) -> u32 {
| ----------- ^^^ expected `u32`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression
2 | price * quantity;
| - help: remove this semicolon to return this value

Если видишь found (), посмотри на последнюю строку функции. В Rust последнее выражение блока без точки с запятой — это его возвращаемое значение. Точка с запятой превращает его в инструкцию, и функция возвращает () (тип «ничего»). Компилятор так и пишет: функция неявно возвращает (), потому что у её тела нет завершающего выражения или return, — и подсказывает убрать точку с запятой:

fn total_price(price: u32, quantity: u32) -> u32 {
price * quantity // без точки с запятой: это значение возвращается
}
fn main() {
println!("Итого: {}", total_price(250, 3));
}

Вывод:

Итого: 750
▶ Попробуй сам

То же самое бывает внутри блоков if и веток match. Подробнее — в уроке Функции в Rust.

Исправление 2: преобразуй String в &str и обратно

Заголовок раздела «Исправление 2: преобразуй String в &str и обратно»

String — владеющая строка, которая может расти. &str — заимствованный взгляд на какой-то текст. Вся история — в уроке Строки. Преобразование в обе стороны короткое:

  • &str в String: .to_string() или String::from(...).
  • String в &str: заимствуй через & (или вызови .as_str()).

Второй случай выглядит как expected &str, found String — обычно когда передаёшь String в функцию, которая принимает &str.

struct User {
name: String,
}
fn greet(name: &str) {
println!("Привет, {name}!");
}
fn main() {
// &str -> String: .to_string() или String::from()
let user = User { name: "Ferris".to_string() };
// String -> &str: заимствуем через & (или вызываем .as_str())
greet(&user.name);
greet(user.name.as_str());
let mut tags: Vec<String> = Vec::new();
tags.push(String::from("rust"));
println!("{tags:?}");
}

Вывод:

Привет, Ferris!
Привет, Ferris!
["rust"]
▶ Попробуй сам

Исправление 3: преобразуй один числовой тип в другой

Заголовок раздела «Исправление 3: преобразуй один числовой тип в другой»
error[E0308]: mismatched types
--> src/main.rs:3:22
|
3 | let count: i32 = names.len();
| --- ^^^^^^^^^^^ expected `i32`, found `usize`
| |
| expected due to this

i32, i64, u32 и usize — разные типы, и Rust их не смешивает. Длины и индексы всегда имеют тип usize. Выбери преобразование:

  • .into() (или i64::from(x)), когда преобразование никогда не теряет данные, например i32 в i64.
  • i64::try_from(x) (или .try_into()), когда значение может не поместиться. Возвращает Result, так что ты сам решаешь, что делать при неудаче.
  • as — быстрое приведение. Оно никогда не падает, но молча обрезает значения, которые не помещаются, так что используй его, только если точно знаешь, что значение маленькое.
fn average(total: i64, count: i64) -> i64 {
total / count
}
fn main() {
let scores: Vec<i32> = vec![80, 95, 70];
let total: i32 = scores.iter().sum();
// i32 -> i64 всегда помещается, поэтому хватает .into()
let total: i64 = total.into();
// usize -> i64 может не поместиться, поэтому try_from сначала проверяет
let count = i64::try_from(scores.len()).unwrap();
println!("Среднее: {}", average(total, count));
// `as` тоже работает, но молча обрезает значения, которые не помещаются
let players = scores.len() as i32;
println!("Игроков: {players}");
}

Вывод:

Среднее: 81
Игроков: 3
▶ Попробуй сам

Часто проще всего поменять аннотацию: let count: usize = names.len();. Глубже эта тема разобрана в уроках Приведение типов и Преобразования (From, Into).

Исправление 4: оберни в Some или достань значение из Option

Заголовок раздела «Исправление 4: оберни в Some или достань значение из Option»
error[E0308]: mismatched types
--> src/main.rs:3:27
|
3 | let position: usize = names.iter().position(|n| *n == "Ben");
| ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `Option<usize>`
| |
| expected due to this

Option<usize> — это не usize. Это «может быть, usize, а может быть, ничего». Такое несовпадение бывает в обе стороны:

  • Expected T, found Option<T>: нужно обработать случай «ничего» через match, if let или unwrap_or(default).
  • Expected Option<T>, found T: функция возвращает Option, так что оберни значение: Some(10) вместо 10.
fn find_discount(code: &str) -> Option<u32> {
if code == "SAVE10" {
return Some(10); // T -> Option<T>: оборачиваем в Some
}
None
}
fn main() {
let names = vec!["Ana", "Ben", "Cy"];
// Option<T> -> T: обрабатываем случай «ничего не нашлось»...
match names.iter().position(|n| *n == "Ben") {
Some(i) => println!("Ben на позиции {i}"),
None => println!("Ben здесь нет"),
}
// ...или выбираем значение по умолчанию через unwrap_or
let discount: u32 = find_discount("HELLO").unwrap_or(0);
println!("Скидка: {discount}%");
}

Вывод:

Ben на позиции 1
Скидка: 0%
▶ Попробуй сам

Компилятор может предложить .expect("REASON"). Это тоже работает, но программа упадёт, если значение окажется None, так что используй его, только когда None действительно невозможен. См. Option.

Исправление 5: пусть все ветки if/else и match имеют один тип

Заголовок раздела «Исправление 5: пусть все ветки if/else и match имеют один тип»
error[E0308]: `if` and `else` have incompatible types
--> src/main.rs:3:52
|
3 | let label = if stock > 0 { "in stock" } else { stock };
| ---------- ^^^^^ expected `&str`, found integer
| |
| expected because of this

Когда if или match выдают значение, Rust должен знать его единственный тип. Поэтому каждая ветка должна давать один и тот же тип. Первая ветка задаёт ожидание, а ошибка указывает на ветку, которая с ним не согласна. Для match сообщение звучит как «match arms have incompatible types» («у веток match несовместимые типы»). Приведи «выбивающуюся» ветку к общему типу:

fn status_text(code: u16) -> String {
match code {
200 => "OK".to_string(),
404 => "Not Found".to_string(),
_ => format!("Неизвестный код {code}"), // каждая ветка — String
}
}
fn main() {
let stock = 0;
let label = if stock > 0 { "в наличии" } else { "распродано" }; // обе &str
println!("{label}");
println!("{}", status_text(404));
println!("{}", status_text(418));
}

Вывод:

распродано
Not Found
Неизвестный код 418
▶ Попробуй сам

Здесь одной ветке нужен был format!, который создаёт String, поэтому и остальные ветки стали String. См. If…else и Match.

  • Чтение ввода пользователя. let age: u32 = input.trim(); даёт «expected u32, found &str». Текст — это не число. Исправление: разбери его: let age: u32 = input.trim().parse().unwrap(); (или обработай ошибку, см. Result).
  • Литералы в Vec<String>. tags.push("rust") даёт «expected String, found &str». Исправление: tags.push("rust".to_string()) или сделай Vec<&str>, если все строки — литералы.
  • Арифметика с разными числовыми типами. total + n, где total — i64, а n — i32, даёт E0308 (и часто рядом E0277). Исправление: сначала преобразуй одну сторону: total + i64::from(n).

Проверь себя

1. У функции написано -> i32, а ошибка говорит "expected i32, found ()". Какая причина вероятнее всего?

2. Функция принимает &str, а у тебя let name = String::from("Ana");. Как её передать?

3. Какое преобразование никогда не теряет данные молча?

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