Rustlings Arena
← Назад в Арену

Распространённые ошибки компилятора Rust

Самые частые ошибки Rust с примерами и способами исправления.

E0382

Использование перемещённого значения

error[E0382]: use of moved value: `s1`

Когда вы присваиваете значение, размещённое в куче (например, String или Vec), другой переменной, владение переходит к новой переменной. Оригинал исчезает — использовать его после этого нельзя. Таким образом система владения Rust устраняет ошибки use-after-free на этапе компиляции.

❌ Broken
let s1 = String::from("hello");
let s2 = s1;          // s1 moves into s2
println!("{}", s1);   // ❌ E0382: s1 was moved
✅ Fixed
let s1 = String::from("hello");
let s2 = s1.clone();  // deep copy — both stay valid
println!("{} {}", s1, s2); // ✅

// Or borrow instead of moving:
let s2 = &s1;
println!("{} {}", s1, s2); // ✅
💡 Tip: Примитивные типы (i32, bool, char, f64) реализуют Copy и дублируются автоматически. Типы из кучи (String, Vec, Box) перемещаются.
E0502

Нельзя заимствовать как мутабельный — уже заимствован как неизменяемый

error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable

Rust разрешает либо много общих (&) ссылок, либо одну эксклюзивную (&mut) — но никогда оба варианта одновременно. Если у вас есть активное неизменяемое заимствование, нельзя взять мутабельное, пока неизменяемое не закончится. Это правило исключает гонки данных по построению.

❌ Broken
let mut v = vec![1, 2, 3];
let first = &v[0];    // immutable borrow
v.push(4);            // ❌ E0502: mutable borrow while immutable exists
println!("{}", first);
✅ Fixed
let mut v = vec![1, 2, 3];
let first = v[0];     // copy the value out (i32 is Copy)
v.push(4);            // ✅ no active borrow
println!("{}", first);

// Or restructure scope:
let mut v = vec![1, 2, 3];
{
    let first = &v[0];
    println!("{}", first);
}   // first's borrow ends here
v.push(4);            // ✅
💡 Tip: Borrow checker консервативен. Если он мешает, попробуйте переместить println раньше, скопировать значение вместо заимствования или использовать .clone().
E0499

Нельзя заимствовать как мутабельный более одного раза

error[E0499]: cannot borrow `v` as mutable more than once at a time

В один момент времени может существовать только одна мутабельная ссылка (&mut). Это предотвращает гонки данных в однопоточном коде и является фундаментальным правилом, обеспечивающим гарантии конкурентности в Rust.

❌ Broken
let mut v = vec![1, 2, 3];
let a = &mut v;
let b = &mut v;   // ❌ E0499: second mutable borrow
a.push(4);
b.push(5);
✅ Fixed
let mut v = vec![1, 2, 3];
{
    let a = &mut v;
    a.push(4);
}   // a's borrow ends here
let b = &mut v;   // ✅ now safe
b.push(5);
💡 Tip: Используйте области видимости {} для ограничения времени жизни мутабельного заимствования. На практике эта ошибка часто означает необходимость реструктурировать логику, чтобы мутации выполнялись последовательно.
E0308

Несовпадение типов

error[E0308]: mismatched types expected `i32`, found `&i32`

Тип, который компилятор вывел или ожидает, не совпадает с предоставленным. Очень распространено при итерировании (итераторы возвращают ссылки), когда функция возвращает () вместо значения, или при смешивании знаковых и беззнаковых целых чисел.

❌ Broken
// Trailing semicolon returns () instead of i32:
fn double(n: i32) -> i32 {
    n * 2;   // ❌ E0308: returns () not i32
}

// Iterator reference confusion:
let nums = vec![1, 2, 3];
let sum: i32 = nums.iter().sum(); // needs type annotation
✅ Fixed
fn double(n: i32) -> i32 {
    n * 2    // ✅ no semicolon — expression returned
}

// Dereference in closures when iterating:
let nums = vec![1, 2, 3];
let doubled: Vec<i32> = nums.iter().map(|&x| x * 2).collect();
//                                       ^ dereference the &i32
💡 Tip: Завершающая точка с запятой на последней строке функции молча меняет тип возврата на (). Это ошибка №1 у начинающих в Rust.
E0277

Ограничение трейта не выполнено

error[E0277]: `MyType` doesn't implement `std::fmt::Display`

Тип не реализует требуемый трейт. Чаще всего возникает при попытке использовать форматирование {} без Display, сравнить типы без PartialOrd или вызвать методы, требующие определённого трейта.

❌ Broken
struct Point { x: i32, y: i32 }

let p = Point { x: 1, y: 2 };
println!("{}", p);  // ❌ E0277: Point doesn't implement Display
println!("{:?}", p); // also fails — needs Debug
✅ Fixed
#[derive(Debug)]
struct Point { x: i32, y: i32 }

let p = Point { x: 1, y: 2 };
println!("{:?}", p);  // ✅ Debug via #[derive(Debug)]

// Implement Display manually for custom formatting:
use std::fmt;
impl fmt::Display for Point {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "({}, {})", self.x, self.y)
    }
}
println!("{}", p);  // ✅
💡 Tip: Добавьте #[derive(Debug)] для вывода через {:?}. Для вывода через {} реализуйте std::fmt::Display вручную. Большинство недостающих ограничений — это Debug, Display, Clone, PartialEq или PartialOrd.
E0106

Отсутствует спецификатор времени жизни

error[E0106]: missing lifetime specifier expected named lifetime parameter

Когда функция возвращает ссылку и имеет несколько параметров-ссылок, Rust не может вывести, из какого входа берётся выходная ссылка. Нужно указать это с помощью аннотаций времён жизни.

❌ Broken
// Compiler doesn't know if the return borrows from x or y:
fn longest(x: &str, y: &str) -> &str {   // ❌ E0106
    if x.len() > y.len() { x } else { y }
}
✅ Fixed
// 'a says: output lives no longer than the shorter input
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {  // ✅
    if x.len() > y.len() { x } else { y }
}

// Single input → no annotation needed (elision rule):
fn first_word(s: &str) -> &str {  // ✅ lifetime elided
    let bytes = s.as_bytes();
    for (i, &b) in bytes.iter().enumerate() {
        if b == b' ' { return &s[..i]; }
    }
    &s[..]
}
💡 Tip: Аннотации времён жизни — это ограничения, а не длительности. 'a означает «выход не может пережить входы». Большинство времён жизни выводится автоматически (элизия). Явные нужны только тогда, когда компилятор не может определить связь.
E0596

Нельзя заимствовать как мутабельный — не объявлен как мутабельный

error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable

Вы попытались изменить переменную, которая не была объявлена с mut. В Rust переменные неизменяемы по умолчанию — компилятор обеспечивает это, чтобы случайные мутации выявлялись на этапе компиляции.

❌ Broken
let x = 5;
x = 10;        // ❌ E0596: x is immutable

let v = vec![1, 2, 3];
v.push(4);     // ❌ E0596: v is immutable
✅ Fixed
let mut x = 5;   // ✅ mut makes it mutable
x = 10;

let mut v = vec![1, 2, 3];
v.push(4);     // ✅
💡 Tip: Добавьте mut после let. Добавляйте mut только тогда, когда переменная действительно должна изменяться — неизменяемость по умолчанию помогает выявлять ошибки и упрощает понимание кода.
E0384

Нельзя присвоить значение неизменяемой переменной дважды

error[E0384]: cannot assign twice to immutable variable `x`

Похоже на E0596, но вызывается вторым присвоением, а не вызовом метода. Переменная была объявлена без mut, но вы попытались переприсвоить её.

❌ Broken
let x = 5;
x = 10;  // ❌ E0384
✅ Fixed
let mut x = 5;
x = 10;  // ✅

// Shadowing is another option if you want to "replace" the variable:
let x = 5;
let x = 10;  // ✅ shadowing — creates a new binding named x
💡 Tip: Затенение (let x = ...) и мутация (mut x = ...) выглядят похоже, но различны: затенение создаёт новую переменную (и может изменить тип), мутация изменяет ту же привязку.
E0507

Нельзя переместить значение из заимствованного содержимого

error[E0507]: cannot move out of `*s` which is behind a shared reference

Вы попытались переместить значение из ссылки. Нельзя взять владение через &T — ссылка не владеет данными.

❌ Broken
fn print_name(s: &String) {
    let owned = *s;  // ❌ E0507: can't move out of &String
    println!("{}", owned);
}
✅ Fixed
fn print_name(s: &String) {
    let owned = s.clone();  // ✅ clone the data
    println!("{}", owned);
}

// Or just use the reference directly:
fn print_name(s: &String) {
    println!("{}", s);  // ✅ no need to own it
}
💡 Tip: Если нужно только прочитать значение, используйте ссылку напрямую. Если нужна owned копия, вызовите .clone(). Если нужно владение, измените параметр на T вместо &T.
E0004

Неисчерпывающие образцы в match

error[E0004]: non-exhaustive patterns: `None` not covered

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

❌ Broken
let opt: Option<i32> = Some(42);
match opt {
    Some(n) => println!("{}", n),
    // ❌ E0004: None not covered
}
✅ Fixed
match opt {
    Some(n) => println!("{}", n),
    None    => println!("nothing"),  // ✅ exhaustive
}

// Use _ as a catch-all:
match opt {
    Some(n) => println!("{}", n),
    _       => {},  // ✅ handles everything else
}

// Or use if let for a single case:
if let Some(n) = opt {
    println!("{}", n);  // ✅ no else needed
}
💡 Tip: Компилятор точно указывает, какие варианты отсутствуют. _ — это перехватчик всего. if let — сокращение, когда вас интересует только один образец.
E0072

Рекурсивный тип имеет бесконечный размер

error[E0072]: recursive type `List` has infinite size

Рекурсивный тип, в котором каждый вариант напрямую содержит себя, потребовал бы бесконечной памяти. Rust должен знать размер каждого типа на этапе компиляции. Решение — косвенность через Box, который имеет известный размер указателя.

❌ Broken
enum List {
    Cons(i32, List),  // ❌ E0072: infinite size
    Nil,
}
✅ Fixed
enum List {
    Cons(i32, Box<List>),  // ✅ Box has fixed pointer size (8 bytes)
    Nil,
}

let list = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil))));
💡 Tip: Box<T> — простейший указатель на кучу. Он имеет фиксированный размер (ширина одного указателя) независимо от T, разрывая рекурсивное вычисление размера.
E0716

Временное значение освобождено, пока на него есть заимствование

error[E0716]: temporary value dropped while borrowed

Вы создали временное значение, взяли ссылку на него, и временное значение было освобождено до того, как ссылка была использована. Ссылка стала бы висячей — указывающей на освобождённую память.

❌ Broken
let s: &str = &String::from("hello")
    .to_uppercase();  // ❌ E0716: temporary dropped

// Also common with chains:
let name = get_user().name;  // if get_user() returns a temp
✅ Fixed
// Bind the temporary to a variable first:
let upper = String::from("hello").to_uppercase();
let s: &str = &upper;  // ✅ upper lives long enough
println!("{}", s);
💡 Tip: При возникновении этой ошибки введите переменную для хранения промежуточного значения. Время жизни привязки let распространяется до конца блока.
E0515

Нельзя вернуть значение со ссылкой на локальную переменную

error[E0515]: cannot return value referencing local variable `local`

Вы попытались вернуть ссылку на локальную переменную. При возврате функции локальная переменная освобождается, оставляя висячую ссылку. Rust предотвращает это на этапе компиляции.

❌ Broken
fn get_greeting() -> &str {
    let s = String::from("hello");
    &s   // ❌ E0515: s is dropped when function returns
}
✅ Fixed
// Return an owned value:
fn get_greeting() -> String {
    String::from("hello")  // ✅ caller owns it
}

// Or return a &'static str literal:
fn get_greeting() -> &'static str {
    "hello"  // ✅ lives for the entire program
}
💡 Tip: Если нужно вернуть ссылку, она должна исходить из входных данных функции (заимствование из параметра) или иметь тип 'static. В случае сомнений возвращайте owned тип (String, Vec и т.д.).
E0369

Бинарная операция не может быть применена

error[E0369]: binary operation `>` cannot be applied to type `T`

Вы использовали оператор для обобщённого типа T, но T не имеет ограничения трейта, требующего существования этого оператора. Обобщённый код должен явно указывать, какие операции поддерживает тип.

❌ Broken
fn largest<T>(list: &[T]) -> &T {
    let mut biggest = &list[0];
    for item in list {
        if item > biggest {  // ❌ E0369: T doesn't implement PartialOrd
            biggest = item;
        }
    }
    biggest
}
✅ Fixed
fn largest<T: PartialOrd>(list: &[T]) -> &T {  // ✅ add bound
    let mut biggest = &list[0];
    for item in list {
        if item > biggest {
            biggest = item;
        }
    }
    biggest
}
💡 Tip: При получении E0369 сообщение об ошибке указывает, какой трейт нужен: PartialOrd для сравнений, Add для +, Display для {}, и т.д. Добавьте его как ограничение: <T: TraitName>.
E0428

Имя определено несколько раз

error[E0428]: the name `connect` is defined multiple times

Вы определили два элемента (функции, структуры, типы и т.д.) с одинаковым именем в одной области видимости. Rust не допускает этого — имена должны быть уникальны в пределах модуля.

❌ Broken
fn connect() -> String { "tcp".into() }
fn connect() -> String { "udp".into() }  // ❌ E0428: duplicate
✅ Fixed
fn connect_tcp() -> String { "tcp".into() }
fn connect_udp() -> String { "udp".into() }  // ✅ unique names

// Or use a parameter:
fn connect(protocol: &str) -> String { protocol.into() }
💡 Tip: Это также происходит при операторах use, импортирующих конфликтующие имена. Используйте as для переименования: use std::io::Error as IoError;
E0061

Неверное количество аргументов

error[E0061]: this function takes 2 arguments but 3 arguments were supplied

Вы вызвали функцию с большим или меньшим числом аргументов, чем объявлено в её сигнатуре. Rust не поддерживает параметры по умолчанию или вариадические функции (кроме макросов вроде println!).

❌ Broken
fn add(a: i32, b: i32) -> i32 { a + b }

add(1, 2, 3);  // ❌ E0061: 3 args, expected 2
add(1);        // ❌ E0061: 1 arg, expected 2
✅ Fixed
add(1, 2);  // ✅

// For optional parameters, use Option:
fn greet(name: &str, title: Option<&str>) {
    match title {
        Some(t) => println!("{} {}", t, name),
        None    => println!("{}", name),
    }
}
greet("Alice", None);
greet("Bob", Some("Dr."));
💡 Tip: В Rust нет параметров по умолчанию. Используйте Option<T> для необязательных значений или строительные структуры для функций со многими необязательными полями.
E0433

Ошибка разрешения — не найдено в области видимости

error[E0433]: failed to resolve: use of undeclared crate or module `HashMap`

Вы использовали тип или функцию, которые не находятся в области видимости. Для типов стандартной библиотеки вне прелюдии (например, HashMap, BTreeMap, BufReader) нужен явный оператор use.

❌ Broken
let mut map = HashMap::new();  // ❌ E0433: HashMap not in scope
✅ Fixed
use std::collections::HashMap;

let mut map = HashMap::new();  // ✅

// Common imports:
use std::collections::{HashMap, HashSet, BTreeMap};
use std::io::{self, BufRead, Write};
use std::fmt;
💡 Tip: Прелюдия Rust автоматически импортирует распространённые типы (Vec, String, Option, Result и т.д.). Всё остальное требует явного use. Rust Analyzer / rust-analyzer предложит корректный путь use.

Ожидалась точка с запятой / ожидалось выражение

error: expected `;`, found `let` error: expected expression, found keyword `fn`

Синтаксические ошибки — парсер обнаружил что-то неожиданное. Обычно это отсутствующая точка с запятой, незакрытый разделитель, опечатка в ключевом слове или неуместное выражение.

❌ Broken
fn main() {
    let x = 5     // ❌ missing semicolon
    let y = 10;

    fn inner() {} // ❌ nested fn needs to be at statement level
    x + y         // if this is a statement, not a return, add ;
}
✅ Fixed
fn main() {
    let x = 5;     // ✅ semicolon added
    let y = 10;

    fn helper() {}  // ✅ nested fn is valid in Rust
    println!("{}", x + y);
}
💡 Tip: Сообщения об ошибках Rust почти всегда указывают на правильную строку. Если ошибка с точкой с запятой кажется неверной, проверьте строку выше — отсутствующая ; в предыдущем операторе вводит парсер в странное состояние.

Переполнение при вычислении требования

error[E0275]: overflow evaluating the requirement `Box<T>: Sized`

Компилятор застрял в бесконечном цикле, пытаясь доказать ограничение трейта. Чаще всего вызывается рекурсивной реализацией трейта или типом, пытающимся реализовать трейт, требующий самого себя.

❌ Broken
// Generic function calling itself with an incompatible bound:
fn process<T: Clone>(x: T) {
    process(x.clone()); // infinite recursion in type inference
}
✅ Fixed
// Add a base case or restructure logic:
fn process(x: String) {
    if x.is_empty() { return; }
    process(x[1..].to_string());
}
💡 Tip: Эта ошибка почти всегда указывает на рекурсивный тип или петлю ограничений трейта. Упростите ограничения трейтов или добавьте явные параметры типа.

Значение не найдено в данной области видимости

error[E0425]: cannot find value `x` in this scope

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

❌ Broken
{
    let x = 5;
}
println!("{}", x);  // ❌ x is out of scope

for i in 0..10 { }
println!("{}", i);  // ❌ i only lives inside the for loop
✅ Fixed
let x = 5;         // declare outside the block
{
    println!("{}", x); // ✅ x is in scope
}
println!("{}", x); // ✅ still in scope

// Save the last loop value explicitly:
let mut last = 0;
for i in 0..10 { last = i; }
println!("{}", last);  // ✅
💡 Tip: Область видимости в Rust основана на блоках. Переменные существуют только от места объявления до конца охватывающего блока. Переместите объявление выше, если вам нужна более широкая область видимости.
🦀

Готовы начать?

26 бесплатных интерактивных заданий. Без установки. Без регистрации.

Начать изучение Rust →