Библиотека Go для собеса | вопросы с собеседований
Відкрити в Telegram
Вопросы с собеседований по Go и ответы на них. Учиться у нас: clc.to/iEeaZw По рекламе: @proglib_adv Для обратной связи: @proglibrary_feeedback_bot Наши каналы: https://t.me/proglibrary/9197
Показати більше7 454
Підписники
+1424 години
+257 днів
+630 день
Архів дописів
⏰ Уже сегодня в 19:00 (МСК) стартует открытый урок!
Тема:
«Мультиагентные системы: почему большинство архитектур переусложнены»🔥 За 90 минут разберёмся, когда действительно стоит строить мультиагентную систему, а когда она только добавляет сложность, расходы и новые точки отказа. Поговорим о критериях выбора архитектуры, типичных ошибках и ограничениях современных ИИ-агентов, которые важно учитывать ещё до внедрения в продукт. 🎙️ Спикер — Дмитрий Юдин, руководитель AI/ML-направления в Сloud․ru. 🎁 Для всех участников подготовили промокод на скидку 10 000 ₽ на курс «Разработка ИИ-агентов». 👉 Успей присоединиться к уроку
❓ Чем
io.EOF отличается от io.ErrUnexpectedEOF? В каком случае Read должен возвращать EOF, а в каком — ErrUnexpectedEOF
io.EOF — сигнал о штатном конце данных. Read возвращает его когда источник исчерпан и больше нечего читать. Это не ошибка в смысле «что-то пошло не так» — это нормальное завершение потока.
По спецификации io.Reader, EOF может прийти либо вместе с последними байтами (n > 0, err == io.EOF), либо отдельным вызовом (n == 0, err == io.EOF). Оба варианта корректны — поэтому правильный код всегда сначала обрабатывает прочитанные байты, потом проверяет ошибку.
io.ErrUnexpectedEOF — сигнал о том, что поток закончился в неожиданном месте. Источник данных иссяк раньше, чем ожидалось по протоколу или по запросу.
Типичный пример — io.ReadFull. Функция просит ровно len(buf) байт. Если источник вернул EOF до того, как буфер заполнен, ReadFull транслирует это в ErrUnexpectedEOF.
🐸 Библиотека Go для собеса🔥 Чем больше агентов — тем лучше? Не всегда. Уже завтра поговорим о реальных ограничениях на открытом уроке «Мультиагентные системы: почему большинство архитектур переусложнены».
Спикер — Дмитрий Юдин, руководитель AI/ML-направления в Сloud․ru.
После урока вы:
🔹 будете лучше ориентироваться в выборе между одним агентом и мультиагентной системой;
🔹 поймёте, какие архитектурные ошибки встречаются чаще всего;
🔹 получите практические ориентиры для проектирования и внедрения агентных систем;
🔹 будете лучше понимать возможности и ограничения современных ИИ-агентов.
🗓️ 18 июня, 19:00 (МСК)
⏱️ 90 минут
👉 Зарегистрироваться и получить промокод на 10 000 ₽
❓ Разработчик создаёт
time.NewTicker внутри цикла и забывает вызвать ticker.Stop(). Что произойдёт
time.NewTicker регистрирует таймер в runtime heap таймеров. Раньше GC не собирал тикеры автоматически — они жили, пока не будет вызван Stop().
Начиная с Go 1.23 ситуация изменилась: garbage collector теперь может собирать неиспользуемые тикеры, если на них больше нет ссылок. То есть забытый Stop() больше не гарантирует утечку памяти.
⚠️ Важно
Если на тикер всё ещё есть ссылка, например, в цикле или замыкании, он продолжит срабатывать по расписанию, занимать ресурсы и слать значения в канал, который никто не читает.
Это по-прежнему может проявляться как постепенный рост потребления памяти и деградация производительности под нагрузкой — просто теперь причина не всегда в самом факте отсутствия Stop(), а в удержании ссылок.
Как это поймать
go tool pprof с профилем goroutines покажет горутины, заблокированные на чтении из каналов тикеров. Heap-профиль может показать рост объектов, связанных с таймерами.
🐸 Библиотека Go для собеса❓ У вас есть
map[int]Record.Несколько горутин читают и пишут в него одновременно. Что произойдёт
Go рантайм с версии 1.6 содержит детектор конкурентных записей в map. При одновременной записи из нескольких горутин программа завершится с паникой concurrent map writes — намеренно, чтобы не допустить повреждения данных.
Но детектор срабатывает не всегда: он вероятностный. Поэтому полагаться на него как на защиту нельзя.
Правильные решения в зависимости от нагрузки: sync.RWMutex вокруг map, sync.Map для случаев с редкими записями и частыми чтениями, или шардирование — несколько map с отдельными мьютексами.
🐸 Библиотека Go для собеса🤖 Большинство материалов по ИИ-агентам устаревают быстрее, чем многие курсы успевают обновить программу
Инструменты, подходы и фреймворки меняются постоянно. Поэтому важно не просто собрать демо-агента, а понимать архитектуру, ограничения и практики, которые используются в продакшене.
🚀 30 июня стартует курс «Разработка ИИ-агентов».
⏳ До 20 июня действует сниженная цена.
За 8 недель под руководством практиков из бигтеха вы соберёте собственного AI-агента, который работает с API, использует память, подключается к внешним сервисам и решает реальную задачу.
Что разберём:
🔹 архитектуру AI-агентов и надёжный вывод;
🔹 LangGraph и оркестрацию workflow;
🔹 MCP и работу с внешними инструментами;
🔹 RAG-системы;
🔹 AgentOps, observability и evals;
🔹 безопасность и защиту от prompt injection;
🔹 мультиагентные системы и A2A.
На курсе отдельно разбираем вопросы надёжности, безопасности и контроля агентных систем.
👉 Узнать программу и забронировать место со скидкой
❓ Какого типа результат
len и можно ли индексировать срез значением int64
len и cap возвращают int, индексы тоже имеют тип int. Индексировать срез значением int64 напрямую нельзя, нужна конверсия к int. Из-за этого длина среза ограничена размером int на платформе.
🐸 Библиотека Go для собеса❓ Как объединить два отсортированных среза в один
Классическая операция из mergesort. Заводим три указателя:
i для a, j для b, k для результата. Сравниваем текущие элементы обоих срезов и берём меньший. После того как один из срезов закончился, дописываем остаток другого.
func mergeSorted(a, b []int) []int {
res := make([]int, 0, len(a)+len(b))
i, j := 0, 0
for i < len(a) && j < len(b) {
if a[i] <= b[j] {
res = append(res, a[i])
i++
} else {
res = append(res, b[j])
j++
}
}
res = append(res, a[i:]...)
res = append(res, b[j:]...)
return res
}
a[i:]... добавляет хвост одной операцией без лишнего цикла. Сложность O(n + m) по времени, O(n + m) по памяти на результат.
🐸 Библиотека Go для собеса💻 3 курса по цене одного — собери стек для оффера в топовую IT-компанию
Для следующего карьерного шага мало писать код. Работодатели ждут не только знания языка, но и понимания архитектуры, алгоритмов, автоматизации, AI-инструментов и агентных систем.
Одно направление закрывает только часть задачи.
Поэтому сейчас мы предлагаем освоить сразу несколько востребованных навыков — выбери любой курс и получи доступ еще к двум бесплатно 🔥
Собери стек навыков под свою цель:
🔹 подготовка к сильным компаниям (алгоритмы, архитектура);
🔹 переход в AI-направление (ИИ-агенты, AgentOps);
🔹 развитие в ML и Data Science (математика, основы ML);
🔹 новый оффер и рост дохода.
Полученные знания применяешь в работе уже во время обучения.
⏳ Акция действует 48 часов — 13 и 14 июня.
👉 Переходи на сайт, выбирай курсы и оставляй заявку — за 10 минут поможем собрать комплект под твою цель.
❓ Почему большой int64 может исказиться при разборе JSON
JSON по спецификации не имеет строгих типов
int32/int64 — там есть только number, а в Go при разборе encoding/json число сначала парсится в float64, если не указан конкретный тип.
Есть несколько решений:
Использовать строки для больших чисел:
{"id": "9223372036854775807"}
Использовать json.Decoder.UseNumber():
dec := json.NewDecoder(r)
dec.UseNumber()
Кастомный Unmarshal
Читать как json.Number и вручную конвертировать в int64
🐸 Библиотека Go для собеса❓ Что такое sliding window и когда его применять
Sliding window это приём для задач, где нужно найти подмассив или подстроку, удовлетворяющие условию. Вместо перебора всех пар
(i, j) за O(n²) мы поддерживаем окно с двумя границами и двигаем их по условию.
Приём подходит, когда нужно найти минимальную или максимальную длину подмассива с суммой >= target, найти подстроку без повторяющихся символов, найти все анаграммы паттерна в строке. Окно бывает фиксированного размера или динамическим.
// минимальный подмассив с суммой >= target
func minSubarrayLen(target int, nums []int) int {
left, sum, res := 0, 0, len(nums)+1
for right := range nums {
sum += nums[right]
for sum >= target {
if right-left+1 < res {
res = right - left + 1
}
sum -= nums[left]
left++
}
}
if res == len(nums)+1 {
return 0
}
return res
}
🐸 Библиотека Go для собеса❓ Чем отличается деление на ноль для целых чисел и для
float
Для целых типов деление на ноль — это паника. Программа сразу падает с ошибкой division by zero, потому что результат такой операции в рамках целочисленной арифметики не определён.
Для float32 / float64 деление на ноль допустимо и подчиняется стандарту IEEE 754. Вместо ошибки получаются специальные значения: +Inf, -Inf, Nan.
🐸 Библиотека Go для собеса📊 Хотите войти в Data Science, но математика кажется самым сложным этапом?
На практике большинство джунов спотыкаются не о Python, а о математическую базу:
▪️ Теорию вероятностей и статистику
▪️ Линейную алгебру
▪️ Математический анализ
▪️ Комбинаторику
Именно на этих темах строятся машинное обучение, аналитика данных и рекомендательные системы.
Освойте ключевые разделы математики, которые используются в Data Science и Machine Learning на курсе «Математика для Data Science».
Что вас ждет:
🔹 40+ видеолекций и 150+ практических заданий на Python
🔹 Проверка домашних работ и обратная связь от преподавателей
🔹 Подготовка к задачам с технических собеседований
🔹 Программа от преподавателей ВМК МГУ, НИУ ВШЭ и экспертов индустрии
Курс подойдет разработчикам, аналитикам и всем, кто планирует развиваться в Data Science и Machine Learning.
👉 Записаться на бесплатный демо-урок
❓ У вас двунаправленный gRPC-стрим. Сервер пишет данные быстрее, чем клиент читает. Какой подход позволяет корректно обработать эту ситуацию без преждевременного обрыва соединения
time.Sleep — антипаттерн: вы не знаете нужную задержку заранее, и это не масштабируется.
Буферизованный канал здесь работает как предохранитель. Сервер пишет в канал, отдельная горутина читает из него и отправляет клиенту. Если буфер заполнен, то клиент не успевает читать, и это сигнал закрыть стрим с понятной ошибкой вместо того, чтобы копить данные в памяти бесконечно.
Рядом с буферизованным каналом обычно стоят ещё два инструмента. Контекст с дедлайном нужен чтобы не ждать вечно зависшего клиента. Тикер — для периодических keepalive-пингов, чтобы убедиться, что соединение живо и клиент на том конце вообще ещё читает.
🐸 Библиотека Go для собеса❓ Как сдвинуть срез на k позиций вправо
Есть несколько подходов. Самый элегантный через три разворота. Разворачиваем весь срез, потом первые
k элементов, потом оставшиеся. Три операции, каждая O(n), итого O(n). Память O(1).
func rotate(s []int, k int) {
n := len(s)
k = k % n
rev := func(a []int) {
for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
a[i], a[j] = a[j], a[i]
}
}
rev(s)
rev(s[:k])
rev(s[k:])
}
k % n обязателен: если k >= n, сдвиг зацикливается. Без этого для k = n мы получим обратный срез вместо исходного.
🐸 Библиотека Go для собеса❓ Что произойдёт при переполнении целого числа
При переполнении целого числа в Go значение не вызывает ошибку, а «переворачивается» внутри допустимого диапазона. Это поведение называется wrap-around: если значение превышает максимальное, оно продолжает отсчёт с минимального значения, и наоборот.
Поэтому переполнение нужно ловить проверками вручную. В константном выражении компилятор поймает его на этапе сборки, а в рантайме нет.
🐸 Библиотека Go для собеса
❓ Как найти два числа с заданной суммой за O(n)
Наивное решение это два вложенных цикла и O(n²). Оптимальное использует map. Для каждого элемента вычисляем
complement = target - s[i] и смотрим, есть ли он уже в map. Если есть, нашли пару. Если нет, кладём текущий элемент в map и идём дальше.
func twoSum(nums []int, target int) (int, int) {
seen := make(map[int]int) // значение -> индекс
for i, v := range nums {
if j, ok := seen[target-v]; ok {
return j, i
}
seen[v] = i
}
return -1, -1
}
Сначала ищем complement в map, только потом добавляем текущий элемент. Это защищает от ложного совпадения с самим собой при target = 2 * v.
🐸 Библиотека Go для собеса❓ Можно ли присвоить
int переменной типа int64 без преобразования
Нет. Это разные типы, даже если на текущей платформе они одного размера. Неявных числовых преобразований в Go нет, нужна явная конверсия int64(x).
🐸 Библиотека Go для собеса❓ Как удалить дубликаты из отсортированного среза за O(n)
Раз срез отсортирован, все одинаковые значения лежат подряд.
Мы заводим индекс записи, который показывает на последний уникальный элемент, и одним проходом идём дальше по срезу. Как только встречаем значение, отличное от того, что уже записано, сдвигаем индекс записи и кладём туда новый элемент. Получается классический приём медленного и быстрого указателя, всё в один проход за O(n) и без дополнительной памяти.
В конце мы возвращаем срез, обрезанный по длине индекса записи.
func dedup[T comparable](s []T) []T {
if len(s) < 2 {
return s
}
w := 1
for i := 1; i < len(s); i++ {
if s[i] != s[w-1] {
s[w] = s[i]
w++
}
}
return s[:w]
}
🐸 Библиотека Go для собеса❓ Какого размера тип
int
Он платформозависимый. На современных платформах занимает 64 бита, на старых 32. Спецификация гарантирует только то, что это либо 32, либо 64 бита, поэтому полагаться на конкретный размер нельзя.
🐸 Библиотека Go для собеса