Подготовка к собеседованию — Rust-разработчик
Колода из 136+ карточек с вопросами для собеседования по направлению «Rust-разработчик» — по темам и уровню сложности, с повторениями по вашим оценкам. Посмотрите несколько карточек ниже, затем выберите доступ, чтобы учить всё направление по расписанию в стиле Anki (SM-2).
Новым подписчикам месяца или года может быть доступно 7 дней бесплатно. Условия и сумму подтвердит страница оплаты.
С чего начать
Перед темами владения и заимствования нужно освоить базовый синтаксис Rust. За модулями Rust идут общие вопросы по проектированию систем и поведенческому интервью; проверяйте примеры кода в небольшом проекте на Rust.
Первый проход: Владение, заимствование и лайфтаймы → Трейты, дженерики и диспетчеризация. Начните с базовых вопросов в этих модулях, затем продолжайте по плану. К сложным и senior-вопросам возвращайтесь по требованиям вакансии; все карточки доступны с самого начала.
Общие темы могут входить в несколько направлений и сохраняют одну историю повторений. План ниже отмечает общие основы и профильные модули; выбирайте глубину под свою вакансию.
Что внутри
Все темы направления, сгруппированные так, как вы будете их учить.
Владение, заимствование и лайфтаймы
12 карточекПрофильный модуль
Трейты, дженерики и диспетчеризация
11 карточекПрофильный модуль
Умные указатели и interior mutability
7 карточекПрофильный модуль
Ошибки, коллекции и экосистема
10 карточекПрофильный модуль
Конкурентность: Send/Sync и атомики
8 карточекПрофильный модуль
Async/await и Tokio
10 карточекПрофильный модуль
Unsafe, память и FFI
7 карточекПрофильный модуль
Live coding и прикладные задачи
7 карточекПрофильный модуль
Системный дизайн
29 карточекОбщие основы
Поведенческое интервью
35 карточекОбщие основы
Примеры вопросов
Несколько карточек из колоды — откройте ответ, затем выберите доступ, чтобы учить весь набор по расписанию.
Как Rust гарантирует безопасность памяти без сборщика мусора?
Как Rust гарантирует безопасность памяти без сборщика мусора?
Короткий ответ: Safe Rust сочетает владение, заимствования и проверку типов, предотвращая ошибки памяти без обязательного трассирующего сборщика мусора. Большинство правил ссылок проверяется при компиляции, но часть механизмов безопасности работает в рантайме.
Подробно:
- Владение определяет уничтожение значений.
Dropможет выполнять произвольную очистку: детерминированное уничтожение не означает бесплатную работу без задержек. - Разделяемые ссылки обычно разрешают чтение, эксклюзивные — изменение. Типы с interior mutability дают контролируемые исключения с сохранением безопасности.
- Лайфтаймы ограничивают ссылки так, чтобы они не пережили объекты, на которые указывают.
- Проверки границ могут выполняться в рантайме;
RefCellдинамически проверяет заимствования, аRc/Arcведут счётчики ссылок. Затраты зависят от выбранных типов и операций. - Код
unsafe/FFI обязан вручную соблюдать инварианты безопасности. Safe-код полагается на корректность этих реализаций. Rust не гарантирует отсутствие утечек, deadlock или всех логических ошибок.
Важно: «Нет обязательного трассирующего GC» — верно. «Вся безопасность бесплатна и проверок в рантайме нет» — неверно.
Источники: Interior mutability Rust, unsafe Rust.
В Rust нет наследования классов — чем трейты и композиция заменяют классическое ООП?
В Rust нет наследования классов — чем трейты и композиция заменяют классическое ООП?
Короткий ответ: Трейты дают полиморфизм в стиле интерфейсов — с методами по умолчанию и blanket-реализациями. Супертрейт — это обязательство «реализуй ещё и тот трейт», а не наследование полей и поведения. Переиспользование кода в Rust строится на композиции: вкладываешь тип в структуру и делегируешь (в т. ч. через Deref).
Подробно:
- Полиморфизм — общий интерфейс через
trait+ дефолтные методы; статически (дженерики) или динамически (dyn). - Blanket impl —
impl<T: Display> ToString for TдаётToStringсразу всемDisplay-типам — переиспользование без иерархии. - Супертрейт ≠ наследование —
trait B: Aтребует реализовать иA, но не наследует его состояние; это ограничение, а не «is-a». - Композиция — поля-компоненты + делегирование методов заменяют цепочки классов; «has-a» вместо «is-a».
| ООП-концепция | Аналог в Rust |
|---|---|
| интерфейс | trait |
| абстрактный метод | метод трейта без тела |
| метод по умолчанию | дефолтный метод трейта |
| наследование поведения | композиция + делегирование |
| наследование интерфейса | супертрейт (trait B: A) |
⚠️ Частая ошибка: называть супертрейт «наследованием». Он не тянет за собой поля и реализацию — это лишь требование реализовать другой трейт.
Когда выбирать Box, Rc и Arc? Чем они отличаются?
Когда выбирать Box, Rc и Arc? Чем они отличаются?
Короткий ответ: Box<T> — единоличный владелец, обычная аллокация в куче с нулевой стоимостью разыменования. Rc<T> — разделяемое владение через неатомарный счётчик ссылок, только внутри одного потока. Arc<T> — то же разделяемое владение, но счётчик обновляется атомарно, поэтому его можно шарить между потоками (ценой атомарных операций).
Подробно:
Box<T>— один владелец, счётчика нет. Нужен, когда значение должно лежать в куче: рекурсивные структуры, трейт-объекты (Box<dyn Trait>), большие значения. Разыменование бесплатно.Rc<T>— несколько владельцев в одном потоке.clone()инкрементит strong-счётчик,dropдекрементит; данные освобождаются при достижении нуля. Отдаёт только&T— для мутации кладутRefCellвнутрь.Arc<T>— какRc, но счётчик на атомиках (fetch_add/fetch_sub). РеализуетSend/Sync, годится для многопоточки; для мутации оборачивают вMutex/RwLock.
| Владельцев | Потоков | Тип |
|---|---|---|
| один | — | Box<T> |
| много | один | Rc<T> |
| много | несколько | Arc<T> |
⚠️ Частая ошибка: тянуть Arc везде «на всякий случай». Если владелец один — хватит Box или вообще стека; атомарные операции Arc не бесплатны, а в одном потоке это чистые накладные расходы.
Когда использовать Option, а когда Result<T, E>?
Когда использовать Option
Короткий ответ: Option<T> — когда значение может просто отсутствовать и причина неважна (Some/None). Result<T, E> — когда операция может завершиться ошибкой и вызывающему нужна причина сбоя (Ok/Err).
Подробно:
- Option — моделирует «значения может не быть»:
HashMap::getне нашёл ключ,Vec::firstна пустом векторе.Noneне несёт информации о причине. - Result — моделирует «операция провалилась»:
File::openне нашёл файл,str::parseне смог распарсить.Err(e)объясняет, что пошло не так. - Конвертация — они легко переходят друг в друга:
option.ok_or(err)превращаетNoneвErr, аresult.ok()отбрасывает ошибку и даётOption.
| Тип | Смысл | Типичный API |
|---|---|---|
| Option |
значение может отсутствовать | HashMap::get, Vec::first |
| Result<T, E> | операция может провалиться | File::open, str::parse |
⚠️ Частая ошибка: возвращать Result там, где отсутствие — норма (или Option там, где нужна причина сбоя). «Ключа нет» — это Option, а не ошибка.
Что такое Send и Sync и как компилятор выводит их автоматически?
Что такое Send и Sync и как компилятор выводит их автоматически?
Короткий ответ: Send означает, что владение значением можно безопасно передать в другой поток; Sync — что &T можно безопасно шарить между потоками (по определению T: Sync ⇔ &T: Send). Это авто-трейты (auto traits): компилятор реализует их структурно — тип Send/Sync, если все его поля Send/Sync. Ничего писать руками не надо.
Подробно:
- Структурный вывод — авто-трейт распространяется по полям: структура из
Send-полей самаSend. Сырые указатели (*const T,*mut T) осознанно отказываются от обоих трейтов, из-за чего типы поверх них тоже теряют их. unsafe impl— если ты гарантируешь потокобезопасность вручную (например, обернул сырой указатель в корректную синхронизацию), можноunsafe impl Send for MyType {}. Это утверждение под твою ответственность: компилятор проверить его не может.- Явный отказ — negative impl (
impl !Send for T) доступен только на nightly; такRcпомечен внутриstd. На stable трейт снимают маркерным полем вродеPhantomData<*const ()>(!Send + !Sync).
| Тип | Send | Sync | Почему |
|---|---|---|---|
Rc<T> |
нет | нет | неатомарный счётчик ссылок |
Arc<T> |
да | да | атомарный счётчик (при T: Send + Sync) |
Cell<T> / RefCell<T> |
да | нет | несинхронизированная внутренняя мутабельность |
Mutex<T> |
да | да | синхронизация внутри (при T: Send) |
*mut T |
нет | нет | нет гарантий у сырого указателя |
⚠️ Частая ошибка: думать, что Send и Sync — про «многопоточность типа вообще». Send — про перемещение владения между потоками, Sync — про разделяемый доступ по ссылке; это разные гарантии, и тип может иметь одну без другой.
Что такое Future и почему вызов async fn без .await ничего не запускает?
Что такое Future и почему вызов async fn без .await ничего не запускает?
Короткий ответ: Future — это ленивая стейт-машина, реализующая трейт Future с методом poll. Вызов async fn не запускает работу, а лишь конструирует объект футуры. Код внутри начинает выполняться, только когда футуру опрашивает (poll) исполнитель — то есть когда вы её .await-ите или отдаёте в spawn.
Подробно:
async fnвозвращает значение — типimpl Future<Output = T>. Сам вызов — это конструктор стейт-машины, а не старт вычисления.- Ленивость — в отличие от потока, футура не крутится в фоне сама по себе. Пока её никто не poll-ит, она инертна.
- Кто двигает футуру —
.awaitвнутри другой async-функции илиtokio::spawn, который отдаёт задачу рантайму. Без этого работа не начнётся.
async fn work() { println!("побежали"); }
let fut = work(); // ничего не произошло — печати нет
fut.await; // вот теперь тело реально выполнилось
⚠️ Частая ошибка: ждать, что async fn стартует «как поток» сразу при вызове. Забытый .await — это не запущенная работа, а нередко и предупреждение unused_must_use: «futures do nothing unless you .await or poll them».
Готовы лучше запоминать?
Посмотрите пример, выберите направление и настройте регулярную практику под свою подготовку.
Вопросы об этом направлении
Как готовиться к собеседованию на «Rust-разработчик»?
Учите концепции, которые придётся объяснять, а не только те, что умеете кодить. Направление «Rust-разработчик» в RecallDeck даёт 136+ отобранных вопросов и возвращает каждый по расписанию в стиле Anki (SM-2), основанному на ваших оценках — чтобы тренировать вспоминание и объяснение. Дополняйте повторения кодингом и пробными собеседованиями.
Какие темы охватывает направление «Rust-разработчик»?
Направление «Rust-разработчик» разбито на ключевые области, которые реально проверяют на таких собеседованиях, — по темам и уровню сложности (Concept, Junior, Middle, Senior). Полный план и примеры вопросов можно посмотреть выше до входа.
Помогает ли интервальное повторение в подготовке к «Rust-разработчик»?
Да. Активное вспоминание и честная самооценка помогают закреплять знания. RecallDeck назначает повторения карт по направлению «Rust-разработчик» на основе ваших оценок: знакомые карты возвращаются реже, а сложные получают больше практики.
Можно попробовать направление «Rust-разработчик» до оплаты?
Новым подписчикам месячного или годового тарифа может быть доступен пробный период семь дней со всем направлением «Rust-разработчик» и всеми функциями. Право на него и сумму подтвердит страница оплаты; при повторной подписке списание может быть сразу. Отмените подписку онлайн до конца пробного периода, чтобы избежать первого списания. У вечного доступа пробного периода нет.