Введение: Почему 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;Блоги
Рекомендаций по книгам
Читайте также