Библиотека Go для собеса | вопросы с собеседований
Open in Telegram
Вопросы с собеседований по Go и ответы на них. Покажем, как запустить своего ии-агента: https://clc.to/tvpmD По рекламе: @proglib_adv Для обратной связи: @proglibrary_feeedback_bot Наши каналы: https://t.me/proglibrary/9197
Show more7 430
Subscribers
-124 hours
-137 days
+730 days
Posts Archive
❓ Какие основные типы
context в Go вы знаете
Go предоставляет функции, которые позволяют создавать производные контексты на основе базового
ctx := context.Background()
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel()
• WithCancel — вручную отменяет контекст
• WithTimeout — отменяет через заданный интервал времени
• WithDeadline — отменяет к заданному моменту времени
• WithValue — добавляет пару ключ/значение в контекст (использовать осторожно)
Каждая из этих функций создаёт дочерний контекст, и если родитель будет отменён — отменятся и все дочерние.
🐸 Библиотека Go для собеса❓ Почему ленивую инициализацию важно делать безопасно в многопоточном окружении
Для ленивой инициализации (lazy init) в многопоточной среде можно использовать
sync.Mutex. Это даёт максимальный контроль, но требует аккуратности.
Рабочий пример:
package main
import (
"fmt"
"sync"
)
type singleton struct {
data string
}
var (
instance *singleton
mu sync.Mutex
)
func GetInstance() *singleton {
if instance == nil {
mu.Lock()
defer mu.Unlock()
if instance == nil {
instance = &singleton{data: "initialized"}
}
}
return instance
}
func main() {
s := GetInstance()
fmt.Println("Singleton data:", s.data)
}
Двойная проверка (Double-Checked Locking):
1️⃣ if instance == nil не требует блокировки и отсекает большинство вызовов
2️⃣ внутри mu.Lock() — необходима, чтобы избежать гонки между горутинами, прошедшими первую проверку одновременно
➡️ Рекомендуется применять только когда нужно встроить логику, контроль или откладку внутрь блокировки. В остальных случаях предпочтительнее sync.Once
🐸 Библиотека Go для собеса❓ Какой контекст выбрать:
context.Background() или context.TODO()
Когда вы работаете с Go-контекстами, легко запутаться: оба выглядят одинаково, оба ничего не делают — но используются по-разному.
➖context.Background() — это ваш "нулевой" контекст, от которого принято строить всё остальное. Он используется в main(), в инициализации, в серверах и корневых вызовах. Это базовая точка отсчёта.
➖context.TODO() — маркер, который говорит: "я ещё не знаю, какой контекст здесь будет, но он точно появится".
Такой контекст часто используется в заготовках, шаблонах, временном коде, пока архитектура не определена.
🐸 Библиотека Go для собеса❓ В чём суть и контекст применения Singleton в Go
Паттерн Singleton (Одиночка) — это порождающий паттерн проектирования, цель которого — гарантировать, что структура (или тип) имеет только один экземпляр и предоставляет глобальную точку доступа к нему.
➡️Когда использовать:
• Централизованное управление ресурсами: пул соединений, логгеры, кэш
• Избегание дублирования состояния в памяти
• Работа в многопоточной среде и потокобезопасный доступ к общей структуре
➡️ Когда не стоит использовать:
• Singleton нарушает SRP (единственная ответственность)
• Затрудняет тестирование (невозможно подменить зависимости)
• Провоцирует на глобальные сингл-точки сбоя (если объект упал — всё развалилось)
• Препятствует масштабируемости при переходе к микросервисной архитектуре
➡️ Важно: в Go нет классов и
private конструкторов как в OOP-языках, поэтому реализация синглтона — технически иной подход, обычно через package-level переменные, sync и функции.
🐸 Библиотека Go для собеса🔥 Последняя неделя перед стартом курса по AI-агентам
Старт курса уже 5го числа! Если вы планировали вписаться — сейчас ПОСЛЕДНИЙ шанс забронировать место
На курсе:
— разложим LLM по косточкам: токенизация, SFT, PEFT, инференс
— соберём RAG и научимся оценивать его адекватно
— построим настоящую мультиагентную систему — архитектуру, которая умеет расти
— разберём CoPilot, сломаем через prompt injection (спасибо Максу)
— и наконец, посмотрим, как это работает в MCP и реальных кейсах
📍 Это 5 живых вебинаров + раздатка + домашки + чат с преподавателями
И главное — возможность реально разобраться, как проектировать системы на LLM, а не просто «поиграться с API»
👉 Курс здесь
❓ В чём разница между следующими объявлениями массивов в Go
arr1 := [5]int{}
arr2 := [...]int{1, 2, 3}
arr3 := [...]int{}
1️⃣ вариант — arr1 := [5]int{} — это явная инициализация массива длиной 5, где все элементы будут равны 0, потому что для типа int это значение по умолчанию.
2️⃣ — arr2 := [...]int{1, 2, 3} — использует вывод длины массива: компилятор сам определяет, что длина должна быть 3, так как передано три элемента. Это удобно, когда длина очевидна из контекста.
А вот 3️⃣ arr3 := [...]int{} — это ошибка компиляции. Компилятор не сможет вывести длину массива из пустого литерала, и поэтому такой код не скомпилируется. Чтобы создать пустой массив, нужно использовать явно указанную длину, например: arr3 := [0]int{}.
🐸 Библиотека Go для собеса❓ Чем горутины в Go отличаются от потоков (threads)
Чтобы разобраться в том, как Go реализует параллельное выполнение задач, важно понимать различия между горутинами и потоками. Эти различия влияют на производительность, ресурсы и удобство работы с многозадачностью. Вот основные отличия горутин от потоков в Go:
➡️ Уровень абстракции
Горутины представляют собой абстракцию, встроенную в язык Go, которая позволяет выполнять функции или методы параллельно.
Потоки — это низкоуровневые объекты операционной системы, предназначенные для параллельного выполнения задач.
➡️ Размер стека
Горутины начинают с небольшого стека (около 2 КБ), который может изменяться по мере необходимости.
Потоки обычно имеют фиксированный размер стека, который значительно больше, часто от 1 МБ и выше.
➡️ Создание и переключение
Горутины создаются с помощью ключевого слова go перед вызовом функции и требуют минимальных затрат на создание и переключение контекста.
Потоки стоят дороже как по времени создания, так и по затратам на переключение контекста, поскольку для этого необходимо взаимодействовать с операционной системой.
➡️ Планировщик
Горутины управляются планировщиком Go, работающим в пользовательском пространстве, который распределяет горутины по доступным потокам ОС (обычно один поток на одно ядро процессора).
Потоки управляются планировщиком операционной системы.
➡️ Изоляция
Ошибка в одной горутине (например, паника) может повлиять на остальные горутины в программе.
🐸 Библиотека Go для собеса
❓ Какие изменения в В Go 1.24 коснулись срезов
В Go 1.24 были улучшены возможности работы с срезами через пакет slices. Вот некоторые новые функции:
➖ Поиск элемента:
slices.Contains(s, v) — проверяет наличие элемента в срезе.
➖ Сортировка: slices.Sort(s) — сортирует срезы для любых типов, поддерживающих интерфейс ordered.
➖ Поиск максимума: slices.Max(s) — находит максимальное значение в срезе.
➖ Удаление элементов: slices.Delete(letters, 1, 4) — удаляет элементы в указанном диапазоне.
➖ Сравнение срезов: slices.Compare(a, b) — сравнивает два среза.
➖ slices.Compact: удаляет дубликаты подряд идущих элементов.
➖ slices.Clip: удаляет неиспользуемую емкость, возвращая срез с длиной и емкостью, равными длине.
➖ slices.Clone: возвращает копию среза.
➖ BinarySearch: выполняет бинарный поиск в отсортированном срезе.
🐸 Библиотека Go для собеса❓ В чем заключается механизм embedding в Go
В Go нет традиционного наследования, как в ООП, но есть механизм embedding (встраивание), который позволяет одному типу включать поля и методы другого типа, обеспечивая схожий эффект.
Важно знать следующее:
➡️ Композиция вместо наследования
В Go предпочтение отдается композиции, а не наследованию. Один тип может включать другой, расширяя его функциональность.
➡️ Простота использования
Встроить один тип в другой очень просто — достаточно определить один тип внутри другого.
➡️ Поведение и интерфейсы
Если встроенный тип реализует интерфейс, то и тип, в который он встроен, автоматически реализует этот интерфейс.
type Engine struct {
Power int
Type string
}
type Car struct {
Engine
Brand string
Model string
}
func main() {
c := Car{
Engine: Engine{Power: 150, Type: "Petrol"},
Brand: "Ford",
Model: "Fiesta",
}
fmt.Println(c.Power) // Выведет: 150
}
Встраивание интерфейсов:
type Writer interface {
Write([]byte) (int, error)
}
type Logger struct {
Writer
}
Теперь Logger автоматически реализует интерфейс Writer, если его встроенное поле Writer реализует этот интерфейс.
Важные моменты:
➖Конфликты имен
Если у встроенного и внешнего типов одинаковые поля или методы, приоритет отдается внешнему типу.
➖Неявное поведение
Методы встроенного типа становятся частью внешнего, что может быть неочевидно при чтении кода.
➖Интерфейсы
В Go можно встраивать интерфейсы, создавая сложные интерфейсы на основе существующих.
🐸 Библиотека Go для собеса❓ Какую роль выполняют функции make и new в Go, и чем они отличаются
В Go, несмотря на наличие сборщика мусора, разработчикам всё равно необходимо выделять память для новых переменных. Для этого язык предоставляет две встроенные функции:
new и make.
➡️ Функция new(T) выделяет память для переменной типа T, инициализирует её нулевым значением этого типа и возвращает указатель на эту переменную.
p := new(int)
В этом примере p будет указателем на новый int, инициализированный значением 0.
➡️ Функция make используется для инициализации срезов, карт и каналов, которые являются встроенными типами данных в Go и требуют дополнительной инициализации перед использованием.
• Срезы: при создании среза с помощью make, задаются начальная длина и ёмкость, что позволяет Go заранее выделить нужное количество памяти
s := make([]int, 5, 10)
• Map: при создании map с помощью make осуществляется его инициализация перед добавлением элементов
m := make(map[string]int)
• Каналы: make также используется для создания каналов.
ch := make(chan int)
Основные различия между make и new
➡️ new возвращает указатель на тип, инициализированный нулевым значением этого типа.
➡️ make возвращает инициализированный экземпляр встроенного типа (среза, map или канала), готовый к использованию.
➡️ Обычно, для выделения памяти под переменные используют литералы типа или make. new используется реже и в основном в более специфичных случаях.
🐸 Библиотека Go для собеса🤯 Мы больше года строим мультиагентные системы
Грабли, находки, паттерны, эксперименты — всё это накопилось и в какой-то момент стало жалко держать только у себя.
Никита — рассказывает (и показывает) базу: токенизация, LLM, SFT, PEFT, локальный инференс + RAG и как оценивать его качество.
Диана — как строят мультиагентные системы, какие есть паттерны проектирования и библиотеки.
Макс — про инференс в проде + разберет CoPilot, соберет с вами из кусочков свой копайлот, а затем его сломает через prompt injection. // Макс фанат autogen (а если нет — он вас разубедит в своем классном канале)
Финальным аккордом Дима углубится в MCP и соберет несколько кейсов повзрослее.
Курс тут: https://clc.to/47pgYA
Промокод: datarascals действует до 23:59 29 июня
❓ Какую роль играет ключевое слово defer в Go
defer в Go — это ключевое слово, которое откладывает выполнение функции или метода до завершения текущей функции.
Когда встречается defer, Go добавляет вызов функции в стек отложенных операций и продолжает выполнение текущего кода.
Применения defer:
➖ Закрытие ресурсов. Одно из самых частых применений defer — это гарантированное закрытие ресурсов (например, файлов, сетевых соединений или подключений к базе данных) после их использования.
file, err := os.Open("file.txt")
if err != nil {
// обработка ошибки
}
defer file.Close()
➖ Множественные отложенные вызовы. Вы можете использовать несколько defer в одной функции. Они будут выполняться в обратном порядке (LIFO — Last In, First Out).
func example() {
defer fmt.Println("1")
defer fmt.Println("2")
fmt.Println("Function body")
}
➖ Передача аргументов. Аргументы функции, вызванной через defer, вычисляются в момент вызова defer, а не при фактическом исполнении отложенной функции.
func example(a int) {
defer fmt.Println(a)
a *= 2
return
}
example(5) // Выведет: 5
➖ Использование с паникой. defer часто используется с recover() для обработки или логирования паники, если она возникает в функции.
func mightPanic() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered from panic:", r)
}
}()
// код, который может вызвать панику
}
➖ Зависимость от контекста. Отложенные функции могут иметь доступ к локальным переменным и изменять их значения, что делает defer удобным инструментом для выполнения завершающих операций перед выходом из функции.
➖ Затраты на производительность. Хотя defer очень удобно использовать, в циклах с интенсивными вычислениями его применение может незначительно повлиять на производительность из-за накладных расходов.
🐸 Библиотека Go для собеса🏎 Вы уже сталкивались с «глухими» зависаниями и гонками данных в Go? Настало время взять каналы под контроль!
💻 На открытом уроке «Подводные камни каналов в Go — и как их обходить» 1 июля в 20:00 МСК мы не просто обсудим, что такое каналы:
— покажем реальные кейсы;
— узнаем, где без них не обойтись;
— разберём частые ошибки, которые тормозят ваши сервисы.
🚀 Представьте: ваш сервис обрабатывает запросы параллельно, без блокировок и утечек. Вы глубоко понимаете, как каналы помогают строить конкурентный код и уверенно внедряете это на любом проекте.
👉 Регистрируйтесь сейчас и получите персональную скидку на курс «Golang Developer. Professional»: https://clc.to/0fYV1g
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ru
❓ Как реализованы интерфейсы и как их используют в коде
Интерфейсы — это инструмент для задания поведения объектов. Они позволяют работать с разными типами, обеспечивая гибкость и полиморфизм.
➡️ Определение интерфейса
Интерфейс в Go — это набор методов без реализации.
type Writer interface {
Write([]byte) (int, error)
}
➡️ Реализация интерфейса
Тип реализует интерфейс, если он предоставляет все методы интерфейса. В Go нет необходимости явно указывать реализацию — это происходит неявно.
➡️ Пустой интерфейс
Интерфейс без методов называется пустым (interface{}). Любой тип удовлетворяет этому интерфейсу, что позволяет создавать универсальные функции и структуры.
➡️ Встраивание интерфейсов
Можно комбинировать интерфейсы, например:
type ReadWriter interface {
Reader
Writer
}
➡️ Методы с указателями
Если метод имеет указатель в качестве получателя, он может быть частью интерфейса только если используется указатель на тип.
➡️ Использование интерфейсов
Интерфейсы задают требования к поведению типов, что позволяет функциям работать с различными типами через интерфейс.
➡️ Type assertion и type switch
Для приведения типов и проверки типа значения интерфейса используют операции type assertion и type switch.
➡️ Значение интерфейса
Интерфейс состоит из двух частей: указателя на значение и указателя на таблицу методов этого значения.
➡️ Nil интерфейса
Интерфейс может содержать значение nil. При вызове метода на таком интерфейсе возникнет ошибка.
🐸 Библиотека Go для собеса❓ Как в Go организовано управление памятью
В Go управление памятью полностью автоматизировано благодаря сборщику мусора. Это значит, что вам не нужно вручную выделять и освобождать память, как в C или C++.
Однако важно быть внимательным, особенно при работе с большими структурами данных, чтобы избежать утечек памяти.
Основные моменты:
• Сборщик мусора:
Алгоритм маркировки и освобождения автоматически помечает активные объекты, а затем очищает неактивные, освобождая память.
• Указатели:
В Go можно работать с указателями, но память выделяется при создании объектов и автоматически освобождается. Прямого управления выделением и освобождением через указатели нет.
• Циклические ссылки:
Хотя управление памятью автоматическое, неправильное использование, например, циклические ссылки, может привести к утечкам. Важно следить за ресурсами.
• Срезы:
Это динамические массивы, которые автоматически управляют памятью при изменении их размера.
• Стек и куча:
Память делится на стек (для локальных переменных) и кучу (для долгоживущих объектов). Управление памятью в куче осуществляется сборщиком мусора.
• Escape analysis:
Этот механизм анализирует, должен ли объект находиться на стеке или в куче, что позволяет Go более эффективно управлять памятью.
🐸 Библиотека Go для собеса
Тест для Golang-разработчиков, проверьте свои знания, готовы ли вы к обучению на курсе.
💻 Ответьте на 20 вопросов за 30 минут и проверьте, готовы ли вы к обучению на онлайн-курсе «Golang Developer. Professional» от OTUS. Сейчас Go становится все востребованнее, благодаря своей производительности, масштабируемости и экосистеме.
После 5 месяцев обучения вы сможете:
— Писать production-ready код, многопоточные и конкурентные программы.
— Понимать синтаксис и внутреннее устройство языка Go.
— Разворачивать микросервисы с помощью Docker.
— Проектировать и реализовывать микросервисную архитектуру на Go.
Также вас ждет прокачка навыков на реальных коммерческих кейсах и под руководством экспертов в этой области. Возможна рассрочка.
👉 ПРОЙТИ ТЕСТ
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ru
❓ Какую ошибку можно ожидать при преобразовании интерфейса к несовместимому типу
Если попытаться преобразовать интерфейс к несовместимому типу без проверки, то это приведет к панике.
Когда тип данных в интерфейсе не совпадает с ожидаемым типом, программа вызовет ошибку типа:
panic: interface conversion: <interface type> is <actual type>, not <expected type>Программа завершит выполнение с ошибкой, и никаких дальнейших операций не будет выполнено. Чтобы избежать этого, рекомендуется всегда использовать проверку типа с
ok, чтобы обработать несовпадения типов безопасным способом.
🐸 Библиотека Go для собеса❓ Что такое тип
map в Go
В языке Go map представляет собой встроенный тип данных, реализующий структуру данных "хеш-таблица".
Он позволяет хранить и эффективно извлекать пары ключ-значение, где каждый ключ уникален и связан с определённым значением.
• Создание map
1. Использование функции make:
m := make(map[string]int)
m["a"] = 1
m["b"] = 2
2. Использование литерала карты:
m := map[string]int{
"a": 1,
"b": 2,
}
• Работа с элементами карты
Добавление или обновление значения: m["c"] = 3
Получение значения: value := m["a"]
Проверка наличия ключа: value, exists := m["b"]
Удаление элемента: delete(m, "a")
• Потокобезопасность
Карты в Go не потокобезопасны. Для безопасного доступа из нескольких горутин используйте мьютексы (sync.Mutex) или sync.Map
var mu sync.Mutex
mu.Lock()
m["a"] = 1
mu.Unlock()
🐸 Библиотека Go для собеса❓ Какую роль играют пакеты в Go и почему они важны
В Go пакет — это основная единица организации кода. Он представляет собой набор файлов в одной директории с общим именем, указанным в строке
package в начале каждого файла.
Пакеты позволяют структурировать программу, разделяя её на логические компоненты.
Особенности в пакетах:
• Объявление пакета — каждый файл начинается с директивы package <имя>
• Импорт пакетов — используем директиву import для доступа к функциям и типам из других пакетов
• Структура каталогов — пакет соответствует одной директории. Все файлы пакета должны быть в одной папке
• Экспортируемые элементы пишутся с заглавной буквы экспортируются и доступны в других пакетах, с маленькой — локальны
Пример:
var Pi = 3.14 // экспортируемая
var radius = 5 // неэкспортируемая
• Программы должны содержать пакет main и функцию main()
• Пакеты стандартной библиотеки — fmt и math/rand
• Сторонние пакеты — устанавливаются через go get и импортируются по полному пути
• Модули в Go 1.11+ модули управляют зависимостями пакетов через файл go.mod
• Тесты размещаются в файлах с суффиксом _test.go в том же пакете
🐸 Библиотека Go для собеса❓ В чем разница между массивами и срезами
Массив — это структура данных с фиксированным размером, определенным при создании. Размер массива нельзя изменить.
Как его создать:
[capacity]data_type{element_values}
Срез — это динамическая версия массива с переменной длиной, которая дает большую гибкость.
Как его создать: s := []data_type{element1, element2, ...}
Особенности срезов:
• Для получения длины используйте len(), для емкости — cap().
• Для добавления элементов append().
• Разделение массивов через [first_index:second_index] или s[:].
• Удаление элементов append(s[:i], s[i+1:]...).
• Многомерные срезы содержат в качестве элементов другие срезы, в скобках отмечаются родительские срезы.
• Копирование с помощью copy().
🐸 Библиотека Go для собеса
Available now! Telegram Research 2025 — the year's key insights 
