Как пройти собеседование по Rust: топ вопросов

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

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

Начать курс

Как пройти собеседование по Rust: топ вопросов

Rust — один из самых востребованных языков программирования в 2025 году. Он сочетает производительность C/C++ с безопасностью памяти, что делает его идеальным выбором для системного программирования, веб-сборки и блокчейн-проектов. Однако собеседование по Rust часто пугает кандидатов из-за строгой системы владения, заимствования и времени жизни. В этой статье мы разберём топ-10 вопросов, которые вам обязательно зададут, и покажем правильные ответы с примерами кода.

1. Что такое владение (ownership) и как оно работает?

Владение — это основа Rust. Каждое значение в Rust имеет ровно одного владельца. Когда владелец выходит из области видимости, значение автоматически удаляется.

fn main() {
    let s = String::from("hello"); // s — владелец строки
    let t = s;                     // владение перемещается в t
    // println!("{}", s);          // ОШИБКА: s больше не владеет значением
    println!("{}", t);             // OK
}

Что спрашивают на собеседовании: объясните разницу между перемещением (move) и копированием (copy). Типы, реализующие трейт Copy (например, i32, bool), копируются, а не перемещаются.

2. Как работает заимствование (borrowing)?

Заимствование позволяет временно использовать значение без передачи владения. Есть два типа заимствования: неизменяемое (&T) и изменяемое (&mut T).

fn main() {
    let mut s = String::from("hello");
    let r1 = &s;      // неизменяемое заимствование
    let r2 = &s;      // можно много неизменяемых
    // let r3 = &mut s; // ОШИБКА: нельзя одновременно изменяемое и неизменяемое
    println!("{} {}", r1, r2);
    let r3 = &mut s;  // OK: после того, как r1 и r2 вышли из области видимости
    r3.push_str(" world");
    println!("{}", r3);
}

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

3. Что такое время жизни (lifetimes)?

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

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);
}

Лайфхак: в большинстве случаев компилятор сам выводит время жизни (элизия). Аннотации нужны только для сложных случаев.

4. Что такое трейты (traits) и как их использовать?

Трейты — это аналог интерфейсов в других языках. Они определяют общее поведение для разных типов.

trait Summary {
    fn summarize(&self) -> String;
}

struct Article {
    title: String,
    content: String,
}

impl Summary for Article {
    fn summarize(&self) -> String {
        format!("{}: {}...", self.title, &self.content[..20])
    }
}

fn main() {
    let article = Article {
        title: String::from("Rust"),
        content: String::from("Rust — безопасный и быстрый язык"),
    };
    println!("{}", article.summarize());
}

Важно: трейты могут иметь реализации по умолчанию и могут быть параметризованы (обобщённые трейты).

5. В чём разница между String и &str?

Это один из самых частых вопросов. String — это изменяемая, владеемая строка, хранящаяся в куче. &str — это неизменяемое заимствование строкового среза, которое может ссылаться на String, строковый литерал или часть строки.

fn main() {
    let s1 = String::from("hello");  // String
    let s2: &str = &s1[..2];         // &str — срез "he"
    let s3: &str = "мир";           // строковый литерал — это &str
    println!("{} {}", s2, s3);
}

Совет: всегда используйте &str для параметров функций, если вам не нужно менять строку.

6. Что такое умные указатели (smart pointers)?

Умные указатели — это структуры, которые ведут себя как указатели, но имеют дополнительную логику. Основные из них: Box<T>, Rc<T>, Arc<T>, RefCell<T>.

use std::rc::Rc;

fn main() {
    let a = Rc::new(5);
    let b = Rc::clone(&a);
    let c = Rc::clone(&a);
    println!("Счётчик ссылок: {}", Rc::strong_count(&a)); // 3
}

Ключевая идея: Box для одиночного владения в куче, Rc для множественного владения (только однопоточный), Arc для многопоточного, RefCell для внутренней изменяемости.

7. Как обрабатывать ошибки в Rust?

Rust использует два основных подхода: Result<T, E> для восстанавливаемых ошибок и panic! для невосстанавливаемых.

fn divide(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 {
        Err(String::from("Деление на ноль!"))
    } else {
        Ok(a / b)
    }
}

fn main() {
    match divide(10.0, 0.0) {
        Ok(result) => println!("Результат: {}", result),
        Err(e) => println!("Ошибка: {}", e),
    }
    // Альтернатива с ?
    let result = divide(10.0, 2.0).unwrap_or(0.0);
    println!("{}", result);
}

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

8. Что такое итераторы и замы

Блоги

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