Библиотека Go для собеса | вопросы с собеседований
Відкрити в Telegram
Вопросы с собеседований по Go и ответы на них. Учиться у нас: clc.to/iEeaZw По рекламе: @proglib_adv Для обратной связи: @proglibrary_feeedback_bot Наши каналы: https://t.me/proglibrary/9197
Показати більше7 451
Підписники
+524 години
+137 днів
-1530 день
Архів дописів
🧑💻 Статьи для IT: как объяснять и распространять значимые идеи
Напоминаем, что у нас есть бесплатный курс для всех, кто хочет научиться интересно писать — о программировании и в целом.
Что: семь модулей, посвященных написанию, редактированию, иллюстрированию и распространению публикаций.
Для кого: для авторов, копирайтеров и просто программистов, которые хотят научиться интересно рассказывать о своих проектах.
👉Материалы регулярно дополняются, обновляются и корректируются. А еще мы отвечаем на все учебные вопросы в комментариях курса.
💬 Какие изменения коснулись паттернов HTTP-роутинга в стандартной библиотеке Go 1.22?
☑️ Теперь можно регистрировать обработчики для конкретных HTTP-методов, что ограничивает вызовы обработчика только запросами с указанным методом. Паттерны с методами имеют приоритет над соответствующими без метода.
☑️ Паттерны с подстановочными знаками, например
/items/{id}, поддерживают сегменты URL-пути. Значение сегмента доступно через метод Request.PathValue. Паттерны, оканчивающиеся на ..., соответствуют всем оставшимся сегментам.
☑️ Паттерны, оканчивающиеся на /, соответствуют всем путям с этим префиксом, а для точного соответствия, включая конечный слэш, используется {$}.
☑️ Если паттерны пересекаются, более конкретный имеет приоритет. Нарушения обратной совместимости контролируются полем GODEBUG httpmuxgo121.
👉 Подробнее17 февраля | One Day Offer для Go-разработчиков в Ozon Tech
Это самый быстрый путь в команду мечты Логистики: проходишь короткий технический скрининг и впечатляешь всех на финальном собеседовании.
Бонус для участников: встреча-знакомство с тимлидами — можно увидеть все команды и выбрать свою! Всё это онлайн.
Чтобы попасть на One Day Offer, заяви о себе здесь
Узнать больше о мероприятии, команде и стеке — по этой же ссылке.
💬 Какие проблемы синхронизации могут возникнуть в программе на Go?
• Data race: две или более горутины одновременно обращаются к одной ячейке памяти без какой-либо синхронизации и как минимум одна из них осуществляет запись.
• Deadlock: две или более горутин ожидают друг друга, освобождая ресурсы, и таким образом ни одна из них не может продолжить выполнение.
• Starvation: одна или несколько горутин никогда не получают доступ к ресурсу или к выполнению, потому что другие горутины постоянно захватывают доступ.
• Livelock: горутины активно выполняют операции, но эти операции не приближают их к завершению задачи. Часто это связано с попытками избежать deadlock, которые приводят к бесконечному циклу.
💬 Каким образом расширяется стек горутины в Go?
В Go стек горутины расширяется динамически, используя метод копирования стека. Изначально каждой горутине выделяется небольшой стек, обычно в размере нескольких килобайт. Когда горутина выполняет операции, которые требуют больше памяти стека, чем доступно, Go автоматически расширяет стек.
Процесс расширения стека начинается с обнаружения, что текущий стек горутины полностью заполнен. В этот момент Go выделяет новый стек, размер которого обычно в два раза больше предыдущего. Затем содержимое старого стека копируется в новый, увеличенный стек.
Важным моментом является обновление всех указателей в стеке. Так как адреса в памяти меняются, Go обновляет все указатели, чтобы они ссылались на новое местоположение в расширенном стеке. После копирования старый стек освобождается.
👉 Подробнее
💬 В чем разница между массивами C и срезами Go?
📌 Массивы в C:
1. Статический размер: в C размер массива должен быть известен во время компиляции и не может изменяться во время выполнения. Это означает, что массивы в C являются статическими.
2. Прямой доступ к памяти: массивы в C представляют собой непрерывный блок памяти. Элементы массива хранятся в памяти последовательно, и доступ к ним осуществляется напрямую через указатели и арифметику указателей.
3. Размер и тип: в C размер массива является частью его типа, что означает, что массивы разного размера представляют разные типы данных.
📌 Срезы в Go:
1. Динамический размер: срезы в Go представляют собой динамические, гибкие представления массивов. Срезы могут изменять свой размер во время выполнения, что делает их более гибкими по сравнению с массивами в C.
2. Абстракция над массивами: срез в Go представляет собой структуру, содержащую три компонента: указатель на массив, длину и емкость. Это позволяет срезам предоставлять дополнительную безопасность и удобство по сравнению с прямым использованием массивов.
3. Управление памятью: в Go управление памятью для срезов осуществляется автоматически с помощью сборщика мусора. Это уменьшает риск ошибок, связанных с утечками памяти и неправильным управлением ресурсами.
4. Встроенные функции: в Go существуют встроенные функции для работы со срезами.
📌 Основное отличие между массивами в C и срезами в Go заключается в их гибкости и управлении памятью. Массивы в C статичны и требуют более тщательного управления памятью, в то время как срезы в Go предлагают динамическую структуру с автоматическим управлением памятью и дополнительными удобными функциями для работы с данными.
💬 Какие изменения коснулись цикла for в Go 1.22?
1. Изменение в поведении переменных цикла: в Go 1.21 и ранее, переменные, объявленные в цикле
for, создавались один раз и обновлялись при каждой итерации. Это могло привести к ошибкам, когда одна и та же переменная использовалась в замыканиях. Например:
var funcs []func()
for i := 0; i < 3; i++ {
funcs = append(funcs, func() { fmt.Println(i) })
}
for _, f := range funcs {
f() // Выводит "3" три раза, так как все функции ссылаются на одну и ту же переменную i.
}
В Go 1.22, каждая итерация цикла создает новые переменные:
var funcs []func()
for i := 0; i < 3; i++ {
funcs = append(funcs, func(i int) func() {
return func() { fmt.Println(i) }
}(i))
}
for _, f := range funcs {
f() // Выводит "0", "1", "2", так как каждая функция ссылается на свою копию переменной i.
}
2. Циклы for теперь могут итерировать по целым числам: в Go 1.22 добавлена возможность использовать циклы for для итерации по диапазону целых чисел. Например:
package main
import "fmt"
func main() {
for i := range 10 {
fmt.Println(10 - i) // Выводит числа от 10 до 1.
}
}
В этом примере цикл for используется для итерации по целым числам от 0 до 9, и на каждой итерации выводится число, равное 10 минус текущее значение итератора.💬 Почему для использования горутин в Go не требуется помечать родительскую функцию как async, в отличие от некоторых других C-подобный ЯП?
Горутины в Go используются для реализации конкурентности, и их работа базируется на модели «общих каналов» вместо «общих данных». Это ключевое отличие от моделей асинхронного программирования, используемых в других языках, похожих на C.
В Go, при вызове функции с ключевым словом
go, рантайм автоматически запускает эту функцию в новой горутине, позволяя ей выполняться конкурентно относительно вызывающего кода.
Это отличается от модели, используемой в языках, где async/await используется для явного указания асинхронного выполнения. В Go, всё управление горутинами и их взаимодействие скрыто от разработчика и обрабатывается рантаймом, что упрощает работу с конкурентным кодом.💬 Для каких целей в Go может использоваться пустая структура?
☑️ Пустая структура (
struct{}{}) не занимает места в памяти. Это делает её идеальной для создания коллекций, где важны только ключи, а значения не нужны.
// Использование для уникального набора значений (set)
uniqueSet := make(map[string]struct{})
uniqueSet["value1"] = struct{}{}
// Проверка наличия значения
_, exists := uniqueSet["value1"]
☑️ Пустые структуры часто используются в каналах для передачи сигналов. Это полезно для синхронизации горутин или передачи сигналов без передачи данных.
done := make(chan struct{})
go func() {
// ...
done <- struct{}{} // Отправка сигнала о завершении
}()
<-done // Ожидание сигнала
☑️ Иногда пустые структуры используются как маркеры в сложных структурах данных или для реализации определённых паттернов проектирования, таких как Singleton или Null Object.
type Singleton struct{}
func (s Singleton) DoSomething() {
// Реализация метода
}
var instance Singleton
instance.DoSomething()💬 Поддерживает ли Go операции инкремента и декремента?
В Go поддерживаются операции инкремента (
++) и декремента (--), но есть некоторые особенности:
🔸 В Go операции инкремента и декремента могут использоваться только как самостоятельные операторы. Это означает, что они не могут быть частью более сложных выражений. Например, нельзя использовать: a = b++ или c = ++d.
🔸 Постфиксная форма: в Go поддерживается только постфиксная форма этих операторов (то есть i++ и i--).💬 Какие существуют распространенные паттерны конкурентности в Go?
Паттерны конкурентности в Go обычно строятся вокруг горутин и каналов.
Вот несколько основных паттернов конкурентности, которые широко используются в Go:
🔸 Worker Pools: подразумевает создание нескольких горутин (воркеров) для выполнения задач из очереди.
🔸 Fan-in (собирает данные из множества источников) и Fan-out (распределяет задачи между несколькими обработчиками).
🔸 Pipeline: организация горутин в серию обработчиков, где каждая горутина выполняет определенную подзадачу. Каждая стадия пайплайна читает из одного канала и пишет в другой, формируя цепочку обработки данных.
🔸 Publish/Subscribe: создание механизма, в котором одни горутины (издатели) публикуют сообщения в канал, а другие горутины (подписчики) читают эти сообщения.
🔸 Context Passing: использование пакета
context для управления жизненным циклом и отмены горутин. Это особенно полезно в сетевых приложениях и при выполнении запросов к базам данных.
🔸 ErrGroup: использование пакета errgroup для параллельного выполнения задач с возможностью обработки ошибок и отмены всех задач при возникновении первой ошибки.
🔸 Select Statement: использование оператора select для ожидания нескольких операций с каналами. select позволяет горутине ожидать несколько коммуникационных операций, блокируясь до готовности одной из них.
👉 Подробнее: доклад Go Concurrency Patterns Роба Пайка (слайды) и Advanced Go Concurrency Patterns (слайды) Sameer Ajmani💬 Что из себя представляет тип Stringer в Go?
Stringer — это интерфейс, определённый в стандартной библиотеке fmt. Он содержит один метод String() string.
Когда тип реализует этот интерфейс, он может контролировать своё строковое представление. Это особенно полезно, когда необходимо вывести структурированные данные или для логирования.
Простой пример:
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%s is %d years old", p.Name, p.Age)
}
Тип Person реализует Stringer, так что каждый раз, когда экземпляр Person выводится с использованием функций из пакета fmt, будет использовано кастомное строковое представление, определённое в методе String().Repost from Библиотека Go-разработчика | Golang
🏃 Самоучитель по Go для начинающих. Часть 6. Функции и аргументы. Области видимости. Рекурсия. Defer
В этом уроке рассмотрим функции, аргументы, области видимости, затронем тему указателей, узнаем про рекурсию и её применение в программировании, а также научимся использовать ключевое слово defer.
👉 Читать статью
👉 Часть 1
👉 Часть 2
👉 Часть 3
👉 Часть 4
👉 Часть 5
💬 Для чего предназначен буфер в каналах Go?
Буфер в каналах используется для управления потоком данных между горутинами и предоставляет несколько ключевых преимуществ:
✔️ Асинхронная передача данных: буферизированный канал позволяет отправителю передавать данные без немедленного ожидания получателя. Это означает, что горутина-отправитель может продолжать свою работу после помещения данных в буфер канала, не блокируясь до тех пор, пока буфер не заполнится полностью.
✔️ Уменьшение блокировок: в небуферизированных каналах отправитель и получатель должны быть готовы к обмену данными одновременно, что может привести к блокировкам. Буферизированные каналы снижают вероятность таких блокировок, поскольку они позволяют временно хранить данные до их обработки.
✔️ Контроль потока: буферизированные каналы могут использоваться для контроля потока данных в приложении. Размер буфера определяет, сколько данных может быть отправлено без блокировки, что позволяет более тонко настраивать производительность и ресурсоемкость приложения.
✔️ Упрощение некоторых паттернов конкурентности: в некоторых случаях использование буферизированных каналов может упростить реализацию определенных паттернов конкурентности, таких как worker pools или регулирование нагрузки между горутинами.
💬 Для чего используется дочерний контекст в Go, и как он связан с родительским контекстом? Приведите пример сценария, где это может быть полезно.
Дочерний контекст в Go используется для управления и контроля над отдельными операциями или задачами, которые являются частью большей работы, представленной родительским контекстом.
Он наследует настройки и сигналы отмены от родительского контекста, обеспечивая согласованное и управляемое выполнение операций в рамках одной иерархии задач.
Дочерний контекст создается из существующего родительского контекста с помощью таких функций, как
context.WithCancel, context.WithDeadline, context.WithTimeout и context.WithValue.
📌 Связь с родительским контекстом: если родительский контекст отменяется, все связанные с ним дочерние контексты также отменяются. Это позволяет централизованно управлять группой задач и их временем жизни.
parentCtx, cancel := context.WithCancel(context.Background())
defer cancel()
childCtx, childCancel := context.WithTimeout(parentCtx, 10*time.Second)
defer childCancel()
// Здесь childCtx будет отменен либо по истечению 10 секунд, либо когда будет отменен parentCtx
📌 Простой пример: в веб-сервере, обрабатывающем HTTP-запросы, для каждого запроса создается дочерний контекст от основного контекста сервера. Если сервер должен быть остановлен, отмена основного контекста приведет к отмене всех обрабатываемых запросов, что позволяет корректно и быстро завершить работу сервера.💬 Как в Go осуществляется доступ к полям структуры?
В Go доступ к полям структуры осуществляется с использованием символа '
.'. Каждое поле структуры имеет своё имя, и к этим полям можно обращаться, используя имя экземпляра структуры, за которым следует точка и имя поля.
Поля структуры могут быть как экспортируемыми (с заглавной буквы), так и неэкспортируемыми (со строчной буквы). Экспортируемые доступны вне пакета, в котором определена структура, в то время как неэкспортируемые доступны только внутри пакета.
📌 Простой пример:
package main
import "fmt"
// Определяем структуру Person
type Person struct {
Name string // Экспортируемое поле
age int // Неэкспортируемое поле
}
func main() {
// Создаем экземпляр структуры Person
p := Person{Name: "Alice", age: 30}
// Доступ к экспортируемому полю Name
fmt.Println("Name:", p.Name)
// Доступ к неэкспортируемому полю age (возможен, т. к. мы находимся в том же пакете)
fmt.Println("Age:", p.age)
}
Доступ к полям структуры осуществляется через экземпляр p структуры Person с использованием точки (p.Name и p.age).🚀 Получи базу по созданию микросервисов за 1.5 часа
🗓 3 февраля в 18:00 по МСК пройдет бесплатный открытый урок по созданию микросервисов на Go (как в BigTtech) от старшего разработчика из Авито
Что будет на открытом уроке:
- с нуля разберешься в монолитной и микросервисной архитектуре приложения;
- узнаешь, в чём разница между монолитом и микросервисами, их сильные и слабые стороны;
- поймешь, нужны ли в твоем проекте микросервисы и какие паттерны используют BigTech-компании;
- задашь любые вопросы по разработке микросервисов старшему разработчику из Avito;
➡️ Регистрация на открытый урок
Реклама. Козырев Олег Вячеславович. ИНН - 440315249608.
erid: LjN8KMUxF
🧑💻 Статьи для IT: как объяснять и распространять значимые идеи
Напоминаем, что у нас есть бесплатный курс для всех, кто хочет научиться интересно писать — о программировании и в целом.
Что: семь модулей, посвященных написанию, редактированию, иллюстрированию и распространению публикаций.
Для кого: для авторов, копирайтеров и просто программистов, которые хотят научиться интересно рассказывать о своих проектах.
👉Материалы регулярно дополняются, обновляются и корректируются. А еще мы отвечаем на все учебные вопросы в комментариях курса.
💬 Можно ли преобразовать []T в []interface{}?
Не напрямую. Спецификация Go запрещает это, поскольку эти два типа имеют разное представление в памяти. Необходимо копировать элементы по отдельности в целевой срез.
В примере срез типа
int преобразуется в срез типа interface{}:
t := []int{1, 2, 3, 4}
s := make([]interface{}, len(t))
for i, v := range t {
s[i] = v
}