сеньорчикОткрыть в Telegram
← все вопросывопросы для собеседований · Основы Rust

Вопросы по основам Rust на собеседовании

Базовые вопросы тут не формальность: почти каждый проверяет, не притащил ли кандидат привычки из другого языка. Строка не индексируется по символам, целое переполнение ведёт себя по-разному в debug и release, а let можно объявить дважды — и это не ошибка, а затенение.

60 вопросов в банке·5 подтем·ниже разбор 9

Что спрашивают

Из чего состоит тема

Так тема разложена в тренажёре: движок ведёт прогресс по каждой подтеме отдельно и возвращает те, где вы ошибаетесь.

Разборы подтем

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

Примеры вопросов с разбором

  1. #rs_collections1 / 9
    Vec создан через with_capacity(2), в него положили два элемента и добавляют третий. Что произойдёт?
    A)Выделится новый буфер большего размера, данные переедут в него
    B)Третий элемент запишется за границей буфера — это UB
    C)push вернёт ошибку: ёмкость исчерпана
    D)Vec допишет элемент в тот же буфер: ёмкость лишь подсказка аллокатору
    показать ответ и разбор
    +A)Выделится новый буфер большего размера, данные переедут в него

    // разбор: При нехватке места Vec выделяет новый буфер (обычно вдвое больше), перемещает туда элементы и освобождает старый — амортизированно push остаётся O(1). Из этого следуют два практических вывода: указатели и ссылки на элементы после роста невалидны (borrow checker это и запрещает), а with_capacity экономит перевыделения, когда размер известен заранее.

  2. #rs_match_option2 / 9
    Почему компилятор требует покрыть все варианты в match?
    A)Чтобы оптимизатор построил таблицу переходов вместо цепочки if
    B)Чтобы непокрытый случай нельзя было забыть: это ошибка компиляции
    C)Это требование только для enum из стандартной библиотеки
    D)Чтобы ветки match сходились к одному типу значения
    показать ответ и разбор
    +B)Чтобы непокрытый случай нельзя было забыть: это ошибка компиляции

    // разбор: Исчерпывающий разбор — способ поймать забытый случай на этапе компиляции: добавили вариант в enum, и все match без ветки для него перестают собираться. Отсюда практика избегать ветку _ в разборе своих enum — она глушит именно ту диагностику, ради которой всё и делается.

  3. #rs_modules3 / 9
    Какая видимость у элементов модуля по умолчанию?
    A)Приватная: видно только внутри модуля и его потомков
    B)Публичная: pub нужен лишь для экспорта из библиотеки
    C)Публичная внутри крейта, приватная снаружи
    D)Зависит от места объявления: в lib.rs — публичная, в остальных — приватная
    показать ответ и разбор
    +A)Приватная: видно только внутри модуля и его потомков

    // разбор: Всё приватно, пока не написан pub. Дочерний модуль видит элементы родителя, обратное не работает: родителю нужен pub у потомка. Это и делает модуль настоящей границей инкапсуляции — внутренности не утекают, пока автор явно не решит их показать.

  4. #rs_strings4 / 9
    В чём разница между String и &str?
    A)String владеет буфером в куче и растёт, &str — заимствованный срез UTF-8
    B)String — тип стандартной библиотеки, &str — устаревший тип из ранних версий
    C)String хранит UTF-8, &str — ASCII-строку фиксированной длины
    D)String лежит в куче, &str — только на литерал в бинарнике
    показать ответ и разбор
    +A)String владеет буфером в куче и растёт, &str — заимствованный срез UTF-8

    // разбор: String — владеющий, изменяемый буфер в куче (указатель, длина, ёмкость). &str — невладеющий вид на кусок UTF-8: указатель и длина, без ёмкости. Отсюда правило API: аргументы принимают &str (подойдёт и литерал, и срез String), а возвращают String, когда нужно отдать владение.

  5. #rs_types_vars5 / 9
    Чем shadowing (let x = ...; let x = ...) отличается от объявления let mut x?
    A)Shadowing заводит новую переменную, mut меняет ту же ячейку
    B)Это синонимы: компилятор разворачивает shadowing в mut
    C)Shadowing работает только внутри циклов, mut — везде
    D)Shadowing меняет значение, но запрещает менять тип переменной
    показать ответ и разбор
    +A)Shadowing заводит новую переменную, mut меняет ту же ячейку

    // разбор: Второй let создаёт новую переменную с тем же именем: старая остаётся жить до конца области видимости, но недоступна по имени. Отсюда главное практическое отличие — при shadowing можно сменить тип (let s = "5"; let s: i32 = s.parse().unwrap()), а mut требует того же типа, зато позволяет менять значение в той же ячейке.

  6. #rs_collections6 / 9
    Считаем частоты слов в HashMap<String, i32>. Что делает *map.entry(w).or_insert(0) += 1?
    A)Перезаписывает значение нулём, затем прибавляет единицу
    B)Вставляет 0, если ключа нет, и увеличивает значение по ссылке
    C)Возвращает копию значения, инкремент в карту не попадает
    D)Паникует, если ключ уже есть: or_insert вставляет только новые
    показать ответ и разбор
    +B)Вставляет 0, если ключа нет, и увеличивает значение по ссылке

    // разбор: entry возвращает вход в карту, or_insert кладёт значение по умолчанию только при его отсутствии и в любом случае отдаёт &mut на значение — разыменование и += меняют его на месте. Это идиома «посмотреть и обновить за один поиск»: get + insert искали бы ключ дважды.

  7. #rs_match_option7 / 9
    Что Option<T> даёт по сравнению с nullable-ссылкой из других языков?
    A)Option быстрее в рантайме: проверка на null исчезает
    B)Option разрешает вызывать методы T напрямую, подставляя значение по умолчанию
    C)Option — это указатель с проверкой на ноль в рантайме
    D)Отсутствие значения видно в типе, и компилятор требует его разобрать
    показать ответ и разбор
    +D)Отсутствие значения видно в типе, и компилятор требует его разобрать

    // разбор: Пустоту нельзя не заметить: она часть типа, и добраться до значения без разбора (match, if let, комбинаторы) не выйдет. Дырка «забыл проверить на null» закрывается на этапе компиляции. При этом Option<&T> и Option<Box<T>> благодаря niche-оптимизации занимают столько же, сколько сам указатель.

  8. #rs_modules8 / 9
    Что означают crate::, self:: и super:: в путях?
    A)Внешнюю зависимость, текущий файл и стандартную библиотеку
    B)Корень текущего крейта, текущий модуль и родительский модуль
    C)Публичный API, приватную часть и переэкспорт
    D)Корень воркспейса, текущий крейт и родительский крейт
    показать ответ и разбор
    +B)Корень текущего крейта, текущий модуль и родительский модуль

    // разбор: Пути бывают абсолютные (от crate:: или имени внешнего крейта) и относительные (self::, super:: или просто имя элемента). super:: полезен в тестовом подмодуле — там пишут use super::*, чтобы затащить проверяемый код. Модули при этом не обязаны совпадать с файлами: mod задаёт дерево, файлы лишь удобная раскладка.

  9. #rs_strings9 / 9
    Чему равно "привет".len()?
    A)6 — len возвращает количество символов
    B)7 — шесть символов плюс завершающий нуль
    C)24 — каждый символ хранится как char по 4 байта
    D)12 — len считает байты UTF-8, кириллица занимает по два
    показать ответ и разбор
    +D)12 — len считает байты UTF-8, кириллица занимает по два

    // разбор: len у str и String — длина в байтах, а не в символах: строки хранятся как UTF-8, где кириллическая буква занимает два байта. Символы считают через chars().count(), но это O(n) и всё равно не то же самое, что «видимые знаки»: e с комбинирующим акутом даст два char при одном отображаемом символе.

это 9 из 60

Ещё 51 вопросов по теме — в тренажёре, с движком повторения

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

Частые вопросы