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

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

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

Начать курс

Введение

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

1. Игнорирование правил заимствования (Borrow Checker)

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

Проблема: множественные ссылки

fn main() {
    let mut data = vec![1, 2, 3];
    let ref1 = &data; // неизменяемая ссылка
    let ref2 = &mut data; // изменяемая ссылка — ОШИБКА!
    println!("{} {}", ref1[0], ref2[0]);
}

Компилятор выдаст ошибку: cannot borrow `data` as mutable because it is also borrowed as immutable.

Решение: правильная область видимости

fn main() {
    let mut data = vec![1, 2, 3];
    {
        let ref1 = &data; // неизменяемая ссылка
        println!("{}", ref1[0]);
    } // ref1 выходит из области видимости
    let ref2 = &mut data; // теперь можно
    ref2[0] = 10;
    println!("{}", ref2[0]);
}

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

2. Неправильная работа со строками: String vs &str

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

Проблема: ожидание владения вместо ссылки

fn print_length(s: String) {
    println!("Длина: {}", s.len());
}

fn main() {
    let name = "Алиса".to_string();
    print_length(name);
    println!("Имя: {}", name); // ОШИБКА! name перемещена
}

Решение: используйте &str для параметров

fn print_length(s: &str) {
    println!("Длина: {}", s.len());
}

fn main() {
    let name = "Алиса".to_string();
    print_length(&name); // передаем ссылку
    println!("Имя: {}", name); // теперь работает
}

Правило: если функция не должна забирать владение, используйте &str. Это делает код гибким — вы можете передавать как &String, так и строковые литералы "текст".

3. Ошибки с временем жизни (Lifetimes)

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

Проблема: неявные lifetimes

fn longest(x: &str, y: &str) -> &str { // ОШИБКА: missing lifetime specifier
    if x.len() > y.len() { x } else { y }
}

Решение: добавьте общий lifetime

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

fn main() {
    let s1 = String::from("короткий");
    let s2 = "длинная строка";
    let result = longest(&s1, &s2);
    println!("Самая длинная: {}", result);
}

Аннотация 'a говорит компилятору, что возвращаемая ссылка будет жить не дольше, чем самый короткий из входных параметров. Это гарантирует безопасность памяти.

4. Игнорирование Option и Result

Rust не имеет null. Вместо этого используется Option<T> для возможного отсутствия значения и Result<T, E> для обработки ошибок. Новички часто пытаются разыменовать Option без проверки.

Проблема: паника при unwrap

fn find_user(id: u32) -> Option<String> {
    if id == 1 {
        Some("Анна".to_string())
    } else {
        None
    }
}

fn main() {
    let user = find_user(2);
    println!("{}", user.unwrap()); // ПАНИКА! user = None
}

Решение: используйте match или if let

fn main() {
    let user = find_user(2);
    match user {
        Some(name) => println!("Пользователь: {}", name),
        None => println!("Пользователь не найден"),
    }
    
    // Или с if let для простого случая
    if let Some(name) = find_user(1) {
        println!("Найден: {}", name);
    }
}

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

Заключение

Мы разобрали топ ошибок начинающих Rust разработчиков: нарушение правил заимствования, путаницу между String и &str, проблемы с lifetimes и небезопасную обработку Option/Result. Каждая из этих ошибок легко решается, если понимать философию Rust.

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

Хотите углубить знания? Посетите pythonlib.ru — здесь вы найдете больше материалов по Rust и другим языкам программирования.

Блоги

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