Как пройти собеседование по 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: &st