Вопросы по обработке ошибок и рантайму Go на собеседовании
В Go нет исключений, поэтому обработка ошибок это часть дизайна кода, а не техническая мелочь. На собесе проверяют идиомы: как оборачивают ошибку, когда уместна паника и в каком порядке выполняются отложенные вызовы.
Из чего состоит тема
Так тема разложена в тренажёре: движок ведёт прогресс по каждой подтеме отдельно и возвращает те, где вы ошибаетесь.
- defer: семантика8
- Идиома обработки ошибок8
- Планировщик GMP7
- panic и recover6
- Sentinel и свои ошибки6
- Оборачивание (%w, Is/As)6
- Профилирование (pprof)6
- Сборка мусора6
- Стек и куча6
- Escape analysis4
Разборы подтем
Конспект по каждой: что это, как отвечать вслух, на чём валятся, плюс вопросы для самопроверки.
- Обработка ошибок в Go8 вопросов
- Оборачивание ошибок в Go6 вопросов
- Sentinel и свои типы ошибок в Go6 вопросов
- panic и recover в Go6 вопросов
- defer в Go8 вопросов
- Планировщик Go: модель GMP7 вопросов
- Сборка мусора в Go6 вопросов
- Стек и куча в Go6 вопросов
- Escape analysis в Go4 вопросов
- Профилирование Go: pprof6 вопросов
Примеры вопросов с разбором
- В каком порядке выполнятся три defer в одной функции?A)Строго в порядке объявления: первый отложенный выполнится первымB)В случайном порядке, определяемом планировщикомC)В обратном порядке (LIFO): последний отложенный — первымD)Все одновременно и параллельно при выходе из функции
показать ответ и разбор
+C)В обратном порядке (LIFO): последний отложенный — первым// разбор: defer'ы кладутся в стек и выполняются в порядке LIFO — последний отложенный первым — при выходе из функции (нормальном или из-за паники). Это удобно для симметричного освобождения ресурсов: открыл A, потом B — закроются B, потом A. Спот-чек: цикл с defer напечатал 2 1 0.
- Как Go по соглашению сообщает об ошибке из функции?A)Бросает исключение, которое ловят try/catch выше по стекуB)Возвращает error последним значением, а вызывающий проверяет егоC)Пишет в глобальную errno, как в C, и возвращает −1D)Логирует ошибку сам и возвращает нулевое значение без сигнала наружу
показать ответ и разбор
+B)Возвращает error последним значением, а вызывающий проверяет его// разбор: В Go ошибки — обычные значения: функция возвращает error последним результатом (nil, если всё хорошо), а вызывающий обязан его проверить: if err != nil. Исключений с раскруткой стека для штатных ошибок нет (panic — для по-настоящему исключительного). Отсюда явный, пусть и многословный, поток обработки: ошибку видно прямо в сигнатуре.
- Чем fmt.Errorf("...: %w", err) отличается от того же с %v?A)%w оборачивает (errors.Is/As видят исходную), %v — только текстB)Разницы нет, оба форматируют ошибку в строку одинаковоC)%w печатает полный стек вызовов, а %v — только текст сообщенияD)%w требует, чтобы ошибка была строкой, иначе паникует
показать ответ и разбор
+A)%w оборачивает (errors.Is/As видят исходную), %v — только текст// разбор: Глагол %w в fmt.Errorf оборачивает ошибку, сохраняя ссылку на неё в цепочке: errors.Is найдёт по ней sentinel, errors.As извлечёт нужный тип. %v (или %s) лишь вставляет ТЕКСТ ошибки в строку — связь теряется, исходную из обёртки уже не достать. Спот-чек: Is с %w → true, с %v → false. Обычно оборачивают %w, добавляя контекст.
- Что происходит при panic, если его никто не перехватил?A)Текущая горутина просто продолжает со следующей строки после паникиB)Стек разворачивается с выполнением defer'ов, затем вся программа падаетC)Паникует только текущая функция, а вызывающая получает errorD)Рантайм ставит горутину на паузу до ручного возобновления
показать ответ и разбор
+B)Стек разворачивается с выполнением defer'ов, затем вся программа падает// разбор: panic прекращает нормальный ход: начинается разворачивание стека, по пути выполняются отложенные defer'ы (в них и живёт шанс на recover). Если ни один defer не сделал recover, разворачивание доходит до верха горутины и роняет ВСЮ программу с трейсом и ненулевым кодом выхода. panic — не «исключение для потока управления», а сигнал катастрофы.
- Что такое sentinel-ошибка в Go?A)Объявленная заранее переменная-ошибка для сравнения через errors.IsB)Ошибка, которая завершает программу через os.Exit сразу при возникновенииC)Временная ошибка-заглушка, удаляемая перед релизом кодаD)Ошибка, автоматически логируемая рантаймом на стандартный вывод
показать ответ и разбор
+A)Объявленная заранее переменная-ошибка для сравнения через errors.Is// разбор: sentinel — экспортированная переменная-ошибка пакета, объявленная заранее (var ErrNotFound = errors.New("not found")), чтобы вызывающий сверялся с ней через errors.Is. Классика: io.EOF, sql.ErrNoRows. Плюс — стабильный маркер конкретного случая; минус — жёсткая связь пакетов по значению (для богатых данных лучше свой тип и errors.As).
- Что определяет escape analysis компилятора Go?A)Какие переменные в функции нигде не используются и потому удаляемы компиляторомB)Разместить значение на стеке или в куче — по «побегу» ссылки за функциюC)Порядок вычисления аргументов в выраженииD)Какие горутины могут выполняться параллельно без гонок
показать ответ и разбор
+B)Разместить значение на стеке или в куче — по «побегу» ссылки за функцию// разбор: escape analysis — анализ компилятора: если на значение НЕ остаётся ссылок после возврата функции, оно кладётся на стек (дёшево, освобождается само). Если ссылка «убегает» наружу (возвращается указатель, кладётся в интерфейс/замыкание/канал), значение переносится в кучу — под управление GC. Решения смотрят через go build -gcflags=-m. Спот-чек: &local → moved to heap.
- Как в Go управляют памятью под кучей?A)Вручную через malloc и free, как в CB)Подсчётом ссылок, который программист инкрементит самC)Память вообще не освобождается до конца программыD)Автоматически: сборщик мусора чистит недостижимые объекты
показать ответ и разбор
+D)Автоматически: сборщик мусора чистит недостижимые объекты// разбор: Go — язык со сборкой мусора: рантайм сам находит объекты, до которых больше нет достижимых ссылок, и освобождает их память. Ручных free нет; escape analysis решает, что вообще попадёт в кучу. Это снимает целый класс багов (use-after-free, двойное освобождение) ценой работы GC, которую в Go старались сделать с очень короткими паузами.
- Что верно про стек горутины в Go?A)Стартует с ~2 КБ и растёт при нужде — горутины поэтому дёшевыB)Фиксирован в несколько мегабайт заранее, ровно как стек потока ОСC)Горутины стека не имеют — всё живёт в кучеD)Размер стека задаётся при go и дальше неизменен
показать ответ и разбор
+A)Стартует с ~2 КБ и растёт при нужде — горутины поэтому дёшевы// разбор: Стек горутины начинается крошечным (около 2 КБ) и растёт по мере необходимости (рантайм копирует его в больший сегмент), а при спаде — сжимается. Поэтому можно держать сотни тысяч горутин: память под стек выделяется по факту, а не мегабайтами заранее, как у потока ОС. Это ключевая причина дешевизны горутин.
- Что позволяет собрать пакет net/http/pprof (или runtime/pprof)?A)Статически проверить код на утечки ещё перед сборкойB)Профили рантайма: CPU, куча, число горутин, блокировкиC)Полный дамп исходного кода приложения вместе с покрытием его тестамиD)Логи всех сетевых запросов приложения
показать ответ и разбор
+B)Профили рантайма: CPU, куча, число горутин, блокировки// разбор: pprof собирает профили работающей программы: CPU (где горит время), heap (кто и сколько аллоцирует), goroutine (сколько горутин и где стоят — ловит утечки), block/mutex (на чём ждут). Подключил net/http/pprof — снимаешь через go tool pprof по HTTP. Это рантайм-профилирование, а не статический анализ и не логи.
это 9 из 63
Ещё 54 вопросов по теме — в тренажёре, с движком повторения
Прочитать разбор и ответить самому — разные навыки. В Сеньорчике вопросы идут сессиями, а движок возвращает подтемы, где вы ошибаетесь, пока они не начнут отскакивать. Бесплатно, лимит по энергии.
Частые вопросы
Как правильно оборачивать ошибки?
Через fmt.Errorf с глаголом %w, чтобы сохранить исходную ошибку для errors.Is и errors.As. Контекст добавляют по мере всплытия, но не дублируют одно и то же сообщение на каждом уровне.
Когда уместна паника?
При нарушении инварианта программы, из которого нельзя восстановиться, и при ошибках инициализации. Для ожидаемых ситуаций возвращают error, а recover используют на границе, например в обработчике HTTP.