Golang | Вопросы собесов
4.32K subscribers
28 photos
2 videos
677 links
Download Telegram
🤔 Как проводить тестирование в Go?

Тестирование в Go реализуется с помощью встроенного пакета testing. Тесты пишутся как функции с именами, начинающимися на Test, принимающими параметр *testing.T. Для запуска тестов используется команда go test. Также доступны инструменты для бенчмарков (Benchmark), покрытия кода тестами (-cover) и работы с примерами (Example).

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какие есть особенности синтаксиса получения и записи значений в map?

Карты (maps) представляют собой ассоциативные массивы, которые связывают ключи с соответствующими значениями. Работа с картами включает получение и запись значений, и Go предоставляет удобный синтаксис для этих операций. Рассмотрим особенности синтаксиса получения и записи значений в карты.

🚩Получение значений

🟠Простое получение значения по ключу
Для этого используется синтаксис индексирования
value := myMap[key]


Пример
package main

import "fmt"

func main() {
myMap := map[string]int{
"Alice": 25,
"Bob": 30,
}

value := myMap["Alice"]
fmt.Println("Alice:", value) // Alice: 25
}


🟠Проверка существования ключа
Для этого используется синтаксис двойного присваивания
value, exists := myMap[key]


Пример
package main

import "fmt"

func main() {
myMap := map[string]int{
"Alice": 25,
"Bob": 30,
}

value, exists := myMap["Charlie"]
if exists {
fmt.Println("Charlie:", value)
} else {
fmt.Println("Charlie not found")
}
}


🚩Запись значений

🟠Добавление или обновление значения
Для этого используется синтаксис индексирования:
myMap[key] = value


Пример
package main

import "fmt"

func main() {
myMap := map[string]int{}

// Добавление значений
myMap["Alice"] = 25
myMap["Bob"] = 30

fmt.Println("Map:", myMap) // Map: map[Alice:25 Bob:30]

// Обновление значения
myMap["Alice"] = 26
fmt.Println("Updated Map:", myMap) // Updated Map: map[Alice:26 Bob:30]
}


🟠Удаление значений из карты
Для этого используется встроенная функция delete:
delete(myMap, key)


Пример
package main

import "fmt"

func main() {
myMap := map[string]int{
"Alice": 25,
"Bob": 30,
}

// Удаление значения по ключу
delete(myMap, "Alice")
fmt.Println("Map after deletion:", myMap) // Map after deletion: map[Bob:30]
}


🟠Подводные камни и особенности

🟠Отсутствие ключа
Если ключ отсутствует в карте, при попытке получения значения будет возвращено нулевое значение типа значения карты. Например, для карты map[string]int это будет 0.
        package main

import "fmt"

func main() {
myMap := map[string]int{
"Alice": 25,
}

value := myMap["Bob"] // Ключ "Bob" отсутствует
fmt.Println("Value:", value) // Value: 0
}


🟠Запись в неинициализированную карту
Нельзя записывать значения в nil карту. Это приведет к панике времени выполнения.
        package main

func main() {
var myMap map[string]int // nil карта
myMap["Alice"] = 25 // Вызовет панику: runtime error: assignment to entry in nil map
}


🟠Итерация по карте
Порядок итерации по карте не определен и может различаться между разными запусками программы.
        package main

import "fmt"

func main() {
myMap := map[string]int{
"Alice": 25,
"Bob": 30,
"Carol": 35,
}

for key, value := range myMap {
fmt.Printf("%s: %d\n", key, value)
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что такое воркстилинг (work stealing)?

Work stealing — это стратегия параллельной обработки задач, при которой неактивный поток берёт задачу у перегруженного потока.
Это помогает равномерно распределять нагрузку и повышает эффективность многопоточности. Используется в современных планировщиках, в том числе в языках с горутинами и акторной моделью.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Можно ли использовать один и тот же буфер []byte в нескольких горутинах?

Может быть как безопасным, так и небезопасным, в зависимости от контекста и конкретного использования.

🚩Когда использование небезопасно

package main

import (
"fmt"
"sync"
)

func main() {
var wg sync.WaitGroup
buffer := make([]byte, 10)

for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
buffer[i] = byte(i) // Несколько горутин одновременно модифицируют буфер
}(i)
}

wg.Wait()
fmt.Println("Buffer:", buffer)
}


🚩Безопасное использование одного буфера

🟠Только чтение
Если несколько горутин только читают из буфера, это безопасно. Пример безопасного использования:
package main

import (
"fmt"
"sync"
)

func main() {
var wg sync.WaitGroup
buffer := []byte("Hello, World!")

for i := 0; i < len(buffer); i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
fmt.Printf("buffer[%d]: %c\n", i, buffer[i])
}(i)
}

wg.Wait()
}


🟠Использование мьютексов
Для синхронизации доступа к буферу используйте мьютексы (sync.Mutex). Это гарантирует, что только одна горутина в данный момент модифицирует буфер.
package main

import (
"fmt"
"sync"
)

func main() {
var wg sync.WaitGroup
var mu sync.Mutex
buffer := make([]byte, 10)

for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
mu.Lock()
buffer[i] = byte(i)
mu.Unlock()
}(i)
}

wg.Wait()
fmt.Println("Buffer:", buffer)
}


🟠Копирование буфера
Если каждая горутина должна работать с независимой копией буфера, создавайте копии для каждой горутины.
package main

import (
"fmt"
"sync"
)

func main() {
var wg sync.WaitGroup
buffer := []byte("Hello, World!")

for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
localBuffer := make([]byte, len(buffer))
copy(localBuffer, buffer)
localBuffer[i] = byte(i)
fmt.Printf("Goroutine %d: %s\n", i, localBuffer)
}(i)
}

wg.Wait()
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Сколько весит число в байтах?

Всё зависит от типа числа:
- int, uint — обычно 8 байт на 64-битных системах.
- int32, uint32 — 4 байта.
- int64, uint64 — 8 байт.
- float32 — 4 байта, float64 — 8 байт.
Go явно различает размеры, что позволяет эффективно управлять памятью.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 В чем разница между буферизированными и небуферизированными каналами?

Каналы — это мощные инструменты для обмена данными между горутинами, обеспечивающие синхронизацию и безопасную коммуникацию. Основное различие между буферизированными и небуферизированными каналами заключается в их поведении при отправке и получении данных.

🚩Небуферизированные каналы

Не имеют внутренней емкости, т.е. они не могут хранить значения. Эти каналы требуют, чтобы отправитель и получатель были готовы обмениваться данными одновременно. Если одна сторона не готова, другая будет заблокирована:
Отправка данных в него блокирует отправителя до тех пор, пока получатель не прочитает данные из канала.
Получение данных из него блокирует получателя до тех пор, пока другая горутина не отправит данные в канал.
ch := make(chan int) // Создание небуферизированного канала
go func() {
val := <-ch // Блокируется, ожидая данные
fmt.Println("Received:", val)
}()
ch <- 3 // Блокируется, пока данные не будут получены


🚩Буферизированные каналы

Имеют внутреннюю емкость, что позволяет хранить одно или несколько элементов без непосредственного получателя данных. Отправка или получение данных работает следующим образом:
Отправка блокируется, только если буфер заполнен. До этого момента данные могут быть отправлены без блокировки, даже если получатель не готов их принять.
Получение из буферизированного канала блокируется, только если канал пуст. Если в канале есть данные, получение происходит без блокировки.
ch := make(chan int, 2) // Создание буферизированного канала с емкостью 2
ch <- 1 // Отправка данных без блокировки
ch <- 2 // Отправка данных без блокировки
go func() {
val := <-ch // Получение данных без блокировки
fmt.Println("Received:", val)
}()


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Возможен ли JOIN со вложенными запросами?

Да, JOIN можно использовать со вложенными (subquery) запросами. Варианты:
- JOIN с подзапросом, возвращающим таблицу (SELECT ... FROM (SELECT ...) AS subquery JOIN ...).
- JOIN с подзапросом в ON (SELECT ... FROM table1 JOIN (SELECT ...) AS subquery ON ...).
- Использование подзапроса в WHERE или IN, но это менее эффективно, чем JOIN.
Вложенные запросы могут снижать производительность, поэтому лучше использовать индексы и анализировать EXPLAIN.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Назови агрегатные функции

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

🚩Основные агрегатные функции

🟠Сумма (`SUM`)
суммирует все элементы.
🟠Среднее (`AVG`)
вычисляет среднее значение.
🟠Минимум (`MIN`)
находит минимальный элемент.
🟠Максимум (`MAX`)
находит максимальный элемент.
🟠Количество (`COUNT`)
считает количество элементов.

🚩Примеры реализации в Go

Функция суммы (SUM)
func Sum(nums []int) int {
sum := 0
for _, num := range nums {
sum += num
}
return sum
}


Функция среднего (AVG)
func Average(nums []int) float64 {
if len(nums) == 0 {
return 0
}
return float64(Sum(nums)) / float64(len(nums))
}


Функция минимума (MIN)
func Min(nums []int) int {
if len(nums) == 0 {
panic("empty slice")
}
min := nums[0]
for _, num := range nums {
if num < min {
min = num
}
}
return min
}


Функция максимума (MAX)
func Max(nums []int) int {
if len(nums) == 0 {
panic("empty slice")
}
max := nums[0]
for _, num := range nums {
if num > max {
max = num
}
}
return max
}


Функция подсчёта (COUNT)
func Count(nums []int) int {
return len(nums)
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Как сообщить компилятору, что наш тип реализует интерфейс?

Компилятору не нужно сообщать это явно – соответствие интерфейсу проверяется автоматически. Однако для явной декларации можно использовать конструкцию вида var _ InterfaceName = (*StructName)(nil).


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Для чего используют индексы?

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

🚩Для чего нужны индексы?

🟠Доступ к элементам по их позиции
Индексы позволяют обращаться к конкретным элементам массива, строки или среза. Например, если у нас есть массив чисел, индекс указывает, какой именно элемент извлечь.

🟠Навигация внутри коллекций
С помощью индексов можно перебирать элементы массива, строки или среза, например, используя циклы.

🟠Изменение значений (если структура изменяема)
В изменяемых структурах данных, таких как срезы или массивы, индекс позволяет присвоить новое значение конкретному элементу.

🟠Оптимизация поиска
Индексы упрощают и ускоряют доступ к данным, потому что доступ осуществляется за O(1) (константное время) в массивах или срезах.

🟠Индексы в строках
В строках индексы используются для доступа к конкретным байтам.
package main

import "fmt"

func main() {
str := "Привет"

fmt.Println(str[0]) // 208 (байт, не символ!)
fmt.Printf("%c\n", str[0]) // П (символ, представленный первым байтом UTF-8)
}


🟠Индексы в массивах и срезах
В массивах и срезах индексы используются для извлечения и изменения значений
package main

import "fmt"

func main() {
arr := [5]int{10, 20, 30, 40, 50}

fmt.Println(arr[2]) // 30

// Изменение значения по индексу
arr[2] = 100
fmt.Println(arr) // [10 20 100 40 50]
}


🟠Как использовать индексы в циклах
Обычно индексы используются для итерации по элементам коллекции с помощью цикла for.
package main

import "fmt"

func main() {
nums := []int{10, 20, 30, 40, 50}

for i, v := range nums {
fmt.Printf("Индекс: %d, Значение: %d\n", i, v)
}
}


🟠Индексы и подстроки
Индексы полезны для извлечения подстрок с использованием срезов:
package main

import "fmt"

func main() {
str := "Привет, Мир!"

fmt.Println(str[8:12]) // Мир
}


🚩Ошибки работы с индексами

🟠Выход за границы
Если попытаться обратиться к элементу по индексу, который выходит за пределы коллекции, Go выдаст runtime panic:

   package main

func main() {
nums := []int{1, 2, 3}
fmt.Println(nums[5]) // panic: runtime error: index out of range
}


🟠Работа с многобайтовыми символами в строках
Если неверно учитывать байтовое представление символов UTF-8, можно получить некорректный результат.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какие бывают виды индексов?

1. Primary Index – создается автоматически на первичном ключе таблицы.
2. Unique Index – предотвращает дублирование значений в колонке.
3. Composite Index (составной индекс) – индекс на несколько столбцов.
4. Full-Text Index – используется для быстрого поиска по тексту.
5. Spatial Index – индекс для геоданных (только MyISAM).
6. Clustered Index – хранит строки в отсортированном порядке (InnoDB).
7. Non-Clustered Index – указывает на строки без изменения порядка хранения.
8. Hash Index – используется в MEMORY таблицах, обеспечивает быстрый доступ к данным.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что такое Prometheus?

Prometheus – это мощная система мониторинга с временными рядами (time series), которая собирает метрики из сервисов, хранит их и позволяет строить графики и отправлять алерты.

🚩Ключевые особенности Prometheus

Pull-модель – сам запрашивает метрики у сервисов (в отличие от push-модели, как в StatsD).
Формат временных рядов – каждая метрика привязана ко времени и меткам (labels).
Язык запросов PromQL – позволяет анализировать и агрегировать метрики.
Автодетектирование сервисов – поддержка Kubernetes, Docker, Consul.
Хранение данных в базе TSDB (Time Series Database).
Гибкая система алертов – интеграция с Alertmanager (уведомления в Slack, Telegram и др.).

🚩Как работает Prometheus?

Экспортеры/сервисы предоставляют метрики через HTTP-эндпоинт (/metrics).
Prometheus сам запрашивает данные по расписанию.
Метрики хранятся в базе TSDB.
Можно строить графики в Grafana или запрашивать данные через API.
Алерты отправляются в Alertmanager при достижении пороговых значений.

🚩Пример метрик в Go
Go-сервис может отдавать метрики через HTTP с помощью prometheus/client_golang
package main

import (
"net/http"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)

// Создаём метрику
var httpRequests = prometheus.NewCounter(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total number of HTTP requests",
})

func main() {
// Регистрируем метрику
prometheus.MustRegister(httpRequests)

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
httpRequests.Inc() // Увеличиваем счётчик при каждом запросе
w.Write([]byte("Hello, Prometheus!"))
})

// Эндпоинт для сбора метрик
http.Handle("/metrics", promhttp.Handler())

http.ListenAndServe(":8080", nil)
}


Пример запроса в PromQL
http_requests_total


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 У ресивера имеется звёздочка — что это значит?

Если у метода ресивер с *, это означает, что метод работает с указателем на объект, а значит:
- изменения внутри метода повлияют на оригинальный объект;
- метод может модифицировать поля структуры;
- вызов возможен как на указателе, так и на значении (Go сам "разыменует").
Такой метод можно вызывать и на value, и на pointer — Go сделает автоматическую конвертацию.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Расскажи про паттерн Builder(Строитель)?

Builder – это порождающий паттерн, который используется для пошагового создания сложных объектов. Он удобен, когда объект имеет много параметров и различных конфигураций.

🚩Когда применять Builder?

🟠Сложные объекты
если у объекта много параметров (особенно опциональных).
🟠Читаемость кода
вместо длинного конструктора с кучей аргументов можно вызывать методы-построители.
🟠Иммутабельность
Builder позволяет создать объект инициализированным сразу, без изменения его полей после создания.

🚩Реализация Builder в Go

В Go нет классов, но можно использовать структуры и методы для реализации этого паттерна.
package main

import "fmt"

// Определяем структуру Car
type Car struct {
Brand string
Model string
Color string
Engine string
}

// Определяем "Строителя" для Car
type CarBuilder struct {
car Car
}

// Методы для пошаговой настройки машины
func (cb *CarBuilder) SetBrand(brand string) *CarBuilder {
cb.car.Brand = brand
return cb
}

func (cb *CarBuilder) SetModel(model string) *CarBuilder {
cb.car.Model = model
return cb
}

func (cb *CarBuilder) SetColor(color string) *CarBuilder {
cb.car.Color = color
return cb
}

func (cb *CarBuilder) SetEngine(engine string) *CarBuilder {
cb.car.Engine = engine
return cb
}

// Метод для финальной сборки объекта
func (cb *CarBuilder) Build() Car {
return cb.car
}

// Используем Builder
func main() {
car := CarBuilder{}.
SetBrand("Tesla").
SetModel("Model S").
SetColor("Red").
SetEngine("Electric").
Build()

fmt.Printf("Car: %+v\n", car)
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
📺 Уникальная база IT собеседований

456+
реальных собеседований на программиста, тестировщика, аналитика и прочие IT профы.

Есть собесы от ведущих компаний: Сбер, Яндекс, ВТБ, Тинькофф, Озон, Wildberries и т.д.

🎯 Переходи по ссылке и присоединяйся к базе, чтобы прокачать свои шансы на успешное трудоустройство!
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Где переменная находится при её выделении в горутине?

В Go переменные, объявленные внутри горутины, по умолчанию аллоцируются на стеке этой горутины. Однако компилятор Go использует escape-анализ, чтобы определить:
- Если переменная используется только внутри функции, она остаётся на стеке.
- Если ссылка на переменную передаётся за пределы функции (например, через замыкание), переменная переносится в кучу (heap).


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM