Библиотека Go для собеса | вопросы с собеседований
6.84K subscribers
221 photos
5 videos
1 file
402 links
Вопросы с собеседований по Go и ответы на них.

По рекламе: @proglib_adv

Учиться у нас: https://proglib.io/w/0b524a15

Для обратной связи: @proglibrary_feeedback_bot

Наши каналы: https://t.me/proglibrary/9197
Download Telegram
Что такое ООП и как Go использует эту концепцию

ООП (объектно-ориентированное программирование) организует код вокруг объектов, которые содержат данные и методы для работы с этими данными.
В Go нет классов, но есть структуры (struct), которые выполняют роль классов, и методы, привязанные к этим структурам.


Go использует структуры и методы для реализации ООП, а также композицию и интерфейсы вместо наследования.
Это позволяет поддерживать принципы инкапсуляции и абстракции, но без классической иерархии наследования.

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍52😁2
Что такое Zero value в Go

Zero value это дефолтное состояние переменных. Например, когда вы создаете переменную структуры, все её поля будут автоматически инициализированы своими нулевыми значениями.

В Go переменные всегда инициализируются нулевыми значениями при их создании. Это позволяет безопасно работать с ними, не беспокоясь о возможных неинициализированных значениях.

Значение зависит от типа переменной:
числовые типы (int, float64) — 0
булевые типы — false
для строк — пустая строка ""
указатели, функции, интерфейсы, срезы, карты и каналы — nil
массивы и структуры — нулевые значения соответствующих типов

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
3👍2
В чем разница между массивами и срезами

Массив — это структура данных с фиксированным размером, определенным при создании. Размер массива нельзя изменить.
Как его создать: [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 для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6🔥1
Какую роль играют пакеты в 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 для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5😁4
Что такое тип 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 для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
😁5👍3
Какую ошибку можно ожидать при преобразовании интерфейса к несовместимому типу

Если попытаться преобразовать интерфейс к несовместимому типу без проверки, то это приведет к панике.

Когда тип данных в интерфейсе не совпадает с ожидаемым типом, программа вызовет ошибку типа:
panic: interface conversion: <interface type> is <actual type>, not <expected type>


Программа завершит выполнение с ошибкой, и никаких дальнейших операций не будет выполнено. Чтобы избежать этого, рекомендуется всегда использовать проверку типа с ok, чтобы обработать несовпадения типов безопасным способом.

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥1
Как в Go организовано управление памятью

В Go управление памятью полностью автоматизировано благодаря сборщику мусора. Это значит, что вам не нужно вручную выделять и освобождать память, как в C или C++.
Однако важно быть внимательным, особенно при работе с большими структурами данных, чтобы избежать утечек памяти.

Основные моменты:

• Сборщик мусора:
Алгоритм маркировки и освобождения автоматически помечает активные объекты, а затем очищает неактивные, освобождая память.

• Указатели:
В Go можно работать с указателями, но память выделяется при создании объектов и автоматически освобождается. Прямого управления выделением и освобождением через указатели нет.

• Циклические ссылки:
Хотя управление памятью автоматическое, неправильное использование, например, циклические ссылки, может привести к утечкам. Важно следить за ресурсами.

• Срезы:
Это динамические массивы, которые автоматически управляют памятью при изменении их размера.

• Стек и куча:
Память делится на стек (для локальных переменных) и кучу (для долгоживущих объектов). Управление памятью в куче осуществляется сборщиком мусора.

• Escape analysis:
Этот механизм анализирует, должен ли объект находиться на стеке или в куче, что позволяет Go более эффективно управлять памятью.

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
8🔥3👍1
Как реализованы интерфейсы и как их используют в коде

Интерфейсы — это инструмент для задания поведения объектов. Они позволяют работать с разными типами, обеспечивая гибкость и полиморфизм.

➡️ Определение интерфейса
Интерфейс в 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 для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6🔥1
Какую роль играет ключевое слово 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 для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
🎉8👍6🥱32
🤯 Мы больше года строим мультиагентные системы

Грабли, находки, паттерны, эксперименты — всё это накопилось и в какой-то момент стало жалко держать только у себя.

Никита — рассказывает (и показывает) базу: токенизация, LLM, SFT, PEFT, локальный инференс + RAG и как оценивать его качество.
Диана — как строят мультиагентные системы, какие есть паттерны проектирования и библиотеки.
Макс — про инференс в проде + разберет CoPilot, соберет с вами из кусочков свой копайлот, а затем его сломает через prompt injection. // Макс фанат autogen (а если нет — он вас разубедит в своем классном канале)
Финальным аккордом Дима углубится в MCP и соберет несколько кейсов повзрослее.

Курс тут: https://clc.to/47pgYA
Промокод: datarascals действует до 23:59 29 июня
Какую роль выполняют функции 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 для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12🔥1
В чем заключается механизм 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 для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
6👍5😁1
Какие изменения в В 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 для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍156👏1
Чем горутины в Go отличаются от потоков (threads)

Чтобы разобраться в том, как Go реализует параллельное выполнение задач, важно понимать различия между горутинами и потоками. Эти различия влияют на производительность, ресурсы и удобство работы с многозадачностью. Вот основные отличия горутин от потоков в Go:

➡️ Уровень абстракции
Горутины представляют собой абстракцию, встроенную в язык Go, которая позволяет выполнять функции или методы параллельно.
Потоки — это низкоуровневые объекты операционной системы, предназначенные для параллельного выполнения задач.


➡️ Размер стека
Горутины начинают с небольшого стека
(около 2 КБ), который может изменяться по мере необходимости.
Потоки обычно имеют фиксированный размер стека, который значительно больше, часто от 1 МБ и выше.


➡️ Создание и переключение
Горутины создаются с помощью ключевого слова go перед вызовом функции и требуют минимальных затрат на создание и переключение контекста.
Потоки стоят дороже как по времени создания, так и по затратам на переключение контекста, поскольку для этого необходимо взаимодействовать с операционной системой.


➡️ Планировщик
Горутины управляются планировщиком Go, работающим в пользовательском пространстве, который распределяет горутины по доступным потокам ОС
(обычно один поток на одно ядро процессора).
Потоки управляются планировщиком операционной системы.

➡️ Изоляция
Ошибка в одной горутине
(например, паника) может повлиять на остальные горутины в программе.

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🔥1
В чём разница между следующими объявлениями массивов в 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 для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15🔥311
🔥 Последняя неделя перед стартом курса по AI-агентам

Старт курса уже 5го числа! Если вы планировали вписаться — сейчас ПОСЛЕДНИЙ шанс забронировать место

На курсе:
разложим LLM по косточкам: токенизация, SFT, PEFT, инференс
— соберём RAG и научимся оценивать его адекватно
— построим настоящую мультиагентную систему — архитектуру, которая умеет расти
— разберём CoPilot, сломаем через prompt injection (спасибо Максу)
— и наконец, посмотрим, как это работает в MCP и реальных кейсах

📍 Это 5 живых вебинаров + раздатка + домашки + чат с преподавателями

И главное — возможность реально разобраться, как проектировать системы на LLM, а не просто «поиграться с API»

👉 Курс здесь
В чём суть и контекст применения Singleton в Go

Паттерн Singleton (Одиночка) — это порождающий паттерн проектирования, цель которого — гарантировать, что структура (или тип) имеет только один экземпляр и предоставляет глобальную точку доступа к нему.

➡️Когда использовать:
• Централизованное управление ресурсами: пул соединений, логгеры, кэш
• Избегание дублирования состояния в памяти
• Работа в многопоточной среде и потокобезопасный доступ к общей структуре


➡️ Когда не стоит использовать:
• Singleton нарушает SRP (единственная ответственность)
• Затрудняет тестирование (невозможно подменить зависимости)
• Провоцирует на глобальные сингл-точки сбоя (если объект упал — всё развалилось)
• Препятствует масштабируемости при переходе к микросервисной архитектуре


➡️ Важно: в Go нет классов и private конструкторов как в OOP-языках, поэтому реализация синглтона — технически иной подход, обычно через package-level переменные, sync и функции.

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🌚2👏1
Какой контекст выбрать: context.Background() или context.TODO()

Когда вы работаете с Go-контекстами, легко запутаться: оба выглядят одинаково, оба ничего не делают — но используются по-разному.

context.Background() — это ваш "нулевой" контекст, от которого принято строить всё остальное. Он используется в main(), в инициализации, в серверах и корневых вызовах. Это базовая точка отсчёта.

context.TODO() — маркер, который говорит: "я ещё не знаю, какой контекст здесь будет, но он точно появится".
Такой контекст часто используется в заготовках, шаблонах, временном коде, пока архитектура не определена.

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
🥱32
Почему ленивую инициализацию важно делать безопасно в многопоточном окружении

Для ленивой инициализации (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 для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
4👏1
Какие основные типы context в Go вы знаете

Go предоставляет функции, которые позволяют создавать производные контексты на основе базового
ctx := context.Background()
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel()


WithCancel — вручную отменяет контекст
WithTimeout — отменяет через заданный интервал времени
WithDeadline — отменяет к заданному моменту времени
WithValue — добавляет пару ключ/значение в контекст (использовать осторожно)

Каждая из этих функций создаёт дочерний контекст, и если родитель будет отменён — отменятся и все дочерние.

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6🥱1