Топ-10 ошибок начинающих Rust разработчиков и как их избежать

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: Почему Rust кажется сложным?

Rust — это современный системный язык программирования, который гарантирует безопасность памяти без сборщика мусора. Однако его строгая система типов и уникальная модель владения (ownership) часто ставят в тупик новичков. Если вы только начинаете изучать Rust и постоянно видите красные сообщения от компилятора, не отчаивайтесь — это нормально. В этой статье мы разберем 10 самых частых ошибок начинающих Rust разработчиков и покажем, как их исправить.

1. Игнорирование borrow checker

Самая популярная ошибка — попытка использовать значение после перемещения (move) или создания изменяемой ссылки. Компилятор Rust (borrow checker) строго следит за правилами владения.

Проблема

let s1 = String::from("привет");
let s2 = s1; // s1 перемещено в s2
println!("{}", s1); // Ошибка! s1 больше не владеет данными

Решение

Используйте clone() если нужно скопировать данные, или передавайте ссылки &:

let s1 = String::from("привет");
let s2 = &s1; // заимствование
println!("{} и {}", s1, s2); // Работает!

Совет: Если компилятор ругается на владение — скорее всего, вам нужна ссылка, а не полное перемещение.

2. Путаница между String и &str

Новички часто не понимают разницу между владеющим типом String и срезом &str. Это приводит к ошибкам типов и лишним аллокациям.

Проблема

fn greet(name: &str) {
    println!("Привет, {}!", name);
}

let my_name = String::from("Анна");
greet(my_name); // Ошибка: expected &str, found String

Решение

Передавайте ссылку на String — она автоматически приведется к &str:

greet(&my_name); // Работает благодаря Deref-преобразованию

Используйте String для владения и изменения строк, а &str — для чтения и передачи в функции.

3. Злоупотребление unwrap() и expect()

Методы unwrap() и expect() — это быстрый способ обработать Option или Result, но они вызывают панику при ошибке. В production-коде это недопустимо.

Проблема

let number: Option<i32> = None;
let value = number.unwrap(); // Паника!

Решение

Используйте match или методы unwrap_or, unwrap_or_else:

let value = number.unwrap_or(0); // Вернет 0, если None
// Или с match
match number {
    Some(v) => println!("{}", v),
    None => println!("Значение отсутствует"),
}

Для Result используйте оператор ? для проброса ошибки наверх.

4. Неправильное использование изменяемых ссылок

Правило «одна изменяемая ссылка ИЛИ множество неизменяемых» нарушается постоянно.

Проблема

let mut data = vec![1, 2, 3];
let ref1 = &mut data;
let ref2 = &mut data; // Ошибка!
println!("{}", ref1);

Решение

Ограничьте область видимости изменяемых ссылок с помощью фигурных скобок:

let mut data = vec![1, 2, 3];
{
    let ref1 = &mut data;
    ref1.push(4);
} // ref1 выходит из области видимости
let ref2 = &mut data; // Теперь можно
ref2.push(5);

5. Забытые времена жизни (lifetimes)

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

Проблема

fn get_ref() -> &str {
    let s = String::from("тест");
    &s // Ошибка: возвращаем ссылку на локальную переменную
}

Решение

Либо возвращайте владеющий тип String, либо принимайте ссылку как аргумент:

fn get_ref(s: &str) -> &str {
    s // Все ок, время жизни совпадает с аргументом
}

Совет: На начальном этапе старайтесь избегать возврата ссылок — используйте String или клонирование.

6. Игнорирование ошибок компиляции

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

Пример

Компилятор не просто говорит «ошибка», а предлагает решение: help: consider using 'clone()' here. Читайте подсказки!

7. Неправильная работа с векторами и итераторами

Попытка изменять вектор во время итерации — классика.

Проблема

let mut v = vec![1, 2, 3];
for i in &v {
    v.push(4); // Ошибка! Нельзя изменять вектор во время итерации
}

Решение

Используйте индексный доступ или соберите новые элементы в отдельный вектор:

let mut v = vec![1, 2, 3];
let mut new_items = vec![4, 5];
v.append(&mut new_items);

8. Путаница с типами чисел

Rust строго типизирован. Нельзя просто сложить i32 и u32.

Проблема

let a: i32 = 10;
let b: u32 = 20;
let c = a + b; // Ошибка: mismatched types

Решение

Явно приводите типы через as:

let c = a + b as i32;


                
                

Блоги

Рекомендаций по книгам