uz
Feedback
Библиотека Go для собеса | вопросы с собеседований

Библиотека Go для собеса | вопросы с собеседований

Kanalga Telegram’da o‘tish

Вопросы с собеседований по Go и ответы на них. Учиться у нас: clc.to/iEeaZw По рекламе: @proglib_adv Для обратной связи: @proglibrary_feeedback_bot Наши каналы: https://t.me/proglibrary/9197

Ko'proq ko'rsatish
7 423
Obunachilar
+224 soatlar
-77 kunlar
+730 kunlar
Postlar arxiv
💬Как обрабатывать сигналы Unix в Go? 🔸Обработка сигналов UNIX в Go обычно выполняется с использованием пакета os/signal. 📌Простой пример: 1. Импортируем пакет os/signal и syscall: import ( "os" "os/signal" "syscall" ) 2. Создаем канал, который будет использоваться для получения сигналов: signals := make(chan os.Signal, 1) 3. Используем функцию signal.Notify, чтобы указать, какие сигналы мы хотим обрабатывать: signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM) 4. Используем select в горутине или в основном потоке программы для ожидания сигналов и реагирования на них: select { case sig := <-signals: // обработка сигнала fmt.Printf("received signal %v\n", sig) } 📌Программа ожидает сигналы SIGINT и SIGTERM, и когда она их получит, выведет полученный сигнал. Это базовый пример того, как можно обрабатывать сигналы в Go. 👉 Подробнее

💬Предположим, что мы хотим выполнить код Go в какой-то момент в будущем или повторно через определенный интервал. Что необхо
+1
💬Предположим, что мы хотим выполнить код Go в какой-то момент в будущем или повторно через определенный интервал. Что необходимо использовать в таком случае? 📌Встроенные функции timer и ticker упрощают обе задачи, при этом таймеры предназначены для кейсов, когда мы хотим сделать что-то один раз в будущем, а тикеры — для кейсов, когда мы хотим сделать что-то повторно через определенные промежутки времени. 🔹Таймеры (первый пример): ◆ Таймеры представляют собой способ ожидания определенного времени перед выполнением действия. ◆ Создание таймера выполняется с помощью функции time.NewTimer(); в нее передаем длительность времени, которую необходимо ожидать. ◆ В примере таймер имеет канал C, на который будет отправлено значение после истечения заданного времени. ◆ Также возможно остановить таймер перед его активацией с помощью метода Stop(). 🔹Тикеры (второй пример): ◆ Тикеры используются для выполнения действий через регулярные промежутки времени. ◆ Создание тикера также выполняется с помощью функции в пакете time, в данном случае time.NewTicker(), с указанием интервала между «тиками». ◆ Аналогично таймерам, тикеры имеют канал C, в который отправляется значение на каждом «тике». ◆ Тикеры можно остановить, используя метод Stop(), что предотвратит дальнейшее отправление значений.

💬В чем разница между пакетами и модулями Go? 📌В Go, пакет — это коллекция исходных файлов .go в одной директории и с одинаковой директивой package, в то время как модуль — это дерево пакетов. Имя модуля задаётся в go.mod. 📌Файлы для одного пакета должны находиться в одной директории. Полное имя для пакета строится из имени модуля и пути к директории с файлами. ◆ Например, в go.mod указано module example.org/mylib, тогда все пакеты из модуля example.org/mylib должны быть в дочерних директориях относительно go.moи путь к каталогу определяет имя пакета. ◆ Например, в дереве исходников нашей библиотеки есть файлы в директоории ./cmd/root. Тогда эти файлы должны быть либо с директивой package root, либо package root_test. И полное имя пакета для этих файлов будет либо example.org/mylib/cmd/root, либо example.org/mylib/cmd/root_test (тесты для пакета example.org/mylib/cmd/root).

💬Почему исполняемый файл Go весит больше, чем C? C: #include <stdio.h> int main() { printf("C\n"); } Go: package main import "fmt" func main() { fmt.Println("Go") } Программа на Go тянет за собой большой и толстый пакет main, тогда как программа на С тянет только stdio.

💬Как переобъявить переменные с помощью коротких объявлений? 📌В одной области видимости нельзя переобъявлять переменные, но это можно делать в объявлении нескольких переменных (multi-variable declarations), среди которых хотя бы одна — новая. Переобъявляемые переменные должны располагаться в том же блоке, иначе получится затенённая переменная. 📌Неправильно: func main() { one := 0 one := 1 // ошибка компиляции } 📌Правильно: func main() { one := 0 one, two := 1,2 one,two = two,one }

💬Какова цель функции init() в Go? 📌Функция init() в Go вызывается автоматически при инициализации пакета. В Go нет конструкторов в классическом понимании, как в некоторых других ЯП, но функция init() предлагает возможность выполнять необходимую начальную настройку. 📌Несколько ключевых моментов: 1. Автоматический вызов: функция init() вызывается автоматически перед вызовом main() и не требует явного вызова. 2. Использование: функции init() можно использовать для инициализации глобальных переменных, проверки или установки конфигурации, установки соединений с базами данных и других целей. 3. Несколько функций init(): в одном пакете можно иметь несколько функций init(). Они будут вызваны в том порядке, в котором объявлены в файле. 4. Наследование и зависимости между пакетами: если один пакет зависит от другого, функции init() в зависимом пакете вызываются первыми.

💬Что такое `json:,omitempty` в контексте структур Go? 🔹Если вы читали предыдущий пост, наверняка заметили упоминание аннотации `json:,omitempty`. Например: type fruit struct { Name string Length int `json:,omitempty` } 🔹`json:,omitempty` — это тег JSON для поля в структуре. Если при демаршаллинге данных JSON в структуру это конкретное поле пусто, оно будет игнорироваться. Без тега omitempty будет использоваться значение по умолчанию. 🔹В приведенном выше примере без тега omitempty поле длины будет заполнено значением int 0. Если тип пустого поля — строка, "" (пустая строка) будет значением по умолчанию. Аналогично, значение по умолчанию для логического типа — false, nil для указателя, интерфейса, среза и мапы. 👉 Подробнее

💬Что из себя представляют структурные теги в Go? 🔸Теги структур в Go — это метаданные, прикрепленные к полям структуры, которые могут быть использованы для предоставления дополнительной информации или инструкций внешним пакетам или библиотекам. 🔸Они представляют собой строковые литералы, расположенные в бэктиках (`` ` ``) в объявлении поля структуры. 📌Пример тега структуры в Go: type Person struct { Name string `json:"name"` Age int `json:"age"` } 🔸В этом примере `json:"name"` и `json:"age"` являются тегами структуры для полей `Name` и `Age` соответственно. Эти теги могут быть использованы пакетом `encoding/json` для управления тем, как объекты `Person` сериализуются/десериализуются в/из JSON. 📌Теги структур могут быть использованы для различных целей, включая: 1. Контроль сериализации и десериализации: теги могут указывать, как поля должны быть сериализованы или десериализованы в форматы, такие как JSON или XML. Например, тег `json:"name,omitempty"` указывает, что поле `Name` должно быть сериализовано как `name` в JSON, и если поле пустое, его следует опустить. 2. Валидация данных: теги могут быть использованы для указания правил валидации для полей, например, минимальной или максимальной длины строки. 3. Описания и документация: теги могут содержать документацию или описания полей. 4. Оркестровка баз данных: теги могут быть использованы для маппинга полей структуры на столбцы в базе данных. 5. Другие кастомные обработки: теги могут быть использованы для произвольной обработки кастомными библиотеками или кодом. Для доступа к тегам структуры и их разбора часто используется пакет `reflect`. Этот пакет предоставляет функции для работы с типами и значениями во время выполнения, что позволяет изучать и изменять значения, типы и теги структур во время выполнения.

💬Что из себя представляет стабы (stubs) и моки (mock) в контексте тестирования в Go? 📌Стабы (stubs) и моки (mocks) являются техниками, используемыми для изоляции тестируемого кода от внешних зависимостей во время тестирования в Go. 🔸Стабы (stubs) — это фейковые объекты, которые предоставляют предопределенные ответы на вызовы методов во время тестирования. package main import "fmt" type DatabaseStub struct{} func (db *DatabaseStub) GetUserName(id int) string { return "Alice" } type Database interface { GetUserName(id int) string } func PrintUserName(db Database, id int) { name := db.GetUserName(id) fmt.Println(name) } func main() { dbStub := &DatabaseStub{} PrintUserName(dbStub, 1) } 🔸Моки (mocks) — это более продвинутые фейковые объекты, которые, кроме предоставления предопределенных ответов, также проверяют, как и когда методы были вызваны в тестах, что помогает в проверке взаимодействия между объектами. package main import ( "github.com/stretchr/testify/mock" "testing" ) type DatabaseMock struct { mock.Mock } func (db *DatabaseMock) GetUserName(id int) string { args := db.Called(id) return args.String(0) } func TestPrintUserName(t *testing.T) { dbMock := new(DatabaseMock) dbMock.On("GetUserName", 1).Return("Alice") name := dbMock.GetUserName(1) dbMock.AssertExpectations(t) } 📌В первом примере создается стаб DatabaseStub, который имеет метод GetUserName. Во втором примере создается мок DatabaseMock с использованием библиотеки testify, который проверяет, был ли метод GetUserName вызван с правильным аргументом.

💬В чем разница между context.Background() и context.TODO()? 📌Разница между context.Background() и context.TODO() в основном заключается в их семантическом использовании. 👉context.Background() используется как стартовый контекст, а context.TODO() указывает на то, что контекст будет предоставлен позже, другими словами, когда неясно, какой контекст использовать, или он еще недоступен. С точки зрения функциональности они идентичны.

💬Что такое паника (panic) в Go? Какие операции автоматически возвращают панику и останавливают программу? 📌Паники относятся к категории ошибок, которые не ожидались разработчиком. Паника похожа на исключения в других ЯП, но предназначена только для ошибок времени выполнения, например: 🔸Доступ к индексу за пределами массива/среза: попытка доступа к элементу массива или среза по индексу за пределами его размера приведет к панике. arr := []int{1, 2, 3} fmt.Println(arr[5]) // panic: runtime error: index out of range 🔸Type assertion: неправильное приведение типа с помощью type assertion может вызвать панику. var i interface{} = "hello" fmt.Println(i.(int)) // panic: interface conversion: interface {} is string, not int 🔸Закрытие закрытого канала: попытка закрыть уже закрытый канал вызовет панику. ch := make(chan int) close(ch) close(ch) // panic: close of closed channel 📌Обработка паники Для обработки паники в Go используется конструкция recover. Recover возвращает значение, переданное функции panic, если вызов recover происходит в той же горутине, что и panic. Это часто используется в сочетании с defer, чтобы обеспечить обработку паники и предотвратить завершение всей программы. func main() { defer func() { if r := recover(); r != nil { fmt.Println("Recovered from panic:", r) } }() causePanic() fmt.Println("This line will not be reached") } func causePanic() { panic("This is a panic") } Если функция causePanic вызывает панику, функция defer будет вызвана перед завершением программы. Функция recover затем используется для захвата значения паники и предотвращения завершения программы. 👉 Подробнее

💬Что будет при чтении/записи из закрытого и неинициализированного канала? 📌Чтение из закрытого канала: когда мы пытаемся читать из закрытого канала, происходит следующее: 1. Если в канале остались значения, которые были в него предварительно записаны, операция чтения будет успешной. 2. Если в канале больше нет значений, операция чтения немедленно завершится, и полученное значение будет нулевым значением для типа данных канала. 3. Если канал уже закрыт и в нем нет значений, попытка прочитать из него снова приведет к получению нулевого значения для типа данных канала. ch := make(chan int) close(ch) value, ok := <-ch fmt.Println(value, ok) // 0 false 📌Запись в закрытый канал: попытка записать в закрытый канал вызовет панику. Это означает, что запись в закрытый канал является недопустимой операцией. ch := make(chan int) close(ch) ch <- 42 // Паника 📌Чтение из неинициализированного канала: попытка чтения из неинициализированного (nil) канала вызовет панику. Перед чтением или записью в канал его следует инициализировать с помощью make. var ch chan int value, ok := <-ch // Паника 📌Запись в неинициализированный канал: попытка записи в неинициализированный (nil) канал вызовет панику. Перед чтением или записью в канал его следует инициализировать с помощью make. var ch chan int ch <- 42 // Паника ☝️Важно отслеживать состояния каналов, инициализировать и закрывать их по мере необходимости, чтобы избежать паник и непредсказуемого поведения.

🧑‍💻 Статьи для IT: как объяснять и распространять значимые идеи Напоминаем, что у нас есть бесплатный курс для всех, кто хо
🧑‍💻 Статьи для IT: как объяснять и распространять значимые идеи Напоминаем, что у нас есть бесплатный курс для всех, кто хочет научиться интересно писать — о программировании и в целом. Что: семь модулей, посвященных написанию, редактированию, иллюстрированию и распространению публикаций. Для кого: для авторов, копирайтеров и просто программистов, которые хотят научиться интересно рассказывать о своих проектах. 👉Материалы регулярно дополняются, обновляются и корректируются. А еще мы отвечаем на все учебные вопросы в комментариях курса.

💬Как в Go реализована композиция (агрегация)? Чем она отличается от наследования? 📌В Go композиция достигается путем встраи
💬Как в Go реализована композиция (агрегация)? Чем она отличается от наследования? 📌В Go композиция достигается путем встраивания одной структуры в другую. Например, встраивание одной структуры в другую позволяет получить доступ к полям встроенной структуры, как если бы они были полями внешней структуры. Композиция в Go более гибкая и рекомендуется как альтернатива наследованию. 📌В нашем примере: Определен базовый тип Engine с двумя полями (Power и Type) и методом Start. Определен тип Car, в который встраивается Engine. В функции main создается экземпляр типа Car, и через этот экземпляр вызывается метод Start встроенного типа Engine, а также обращаются к полям встроенного типа Engine. 📌Таким образом, композиция в Go позволяет объединять простые типы в более сложные структуры, сохраняя при этом простоту и ясность кода.

💬Для чего в программах на Go используются функции? Функции в Go играют ключевую роль, предлагая способ организации кода, повторного использования логики и разделения задач на меньшие, управляемые компоненты. Они выполняют несколько важных задач: 1️⃣Модульность и организация кода: функции позволяют разбивать программу на меньшие блоки, улучшая читаемость и понимание кода. Это способствует созданию более структурированных и поддерживаемых программ. 2️⃣Повторное использование кода: после написания функции ее можно использовать многократно в разных частях программы или даже в разных программах. Это сокращает дублирование кода и упрощает обновления. 3️⃣Абстракция и инкапсуляция: функции позволяют абстрагироваться от деталей реализации, сосредоточив внимание на функциональности. Это улучшает понимание кода и уменьшает сложность. Кроме того, функции могут создавать границы, предотвращая воздействие изменений в одной части программы на другие. 4️⃣Тестирование: разбиение программы на функции упрощает процесс тестирования. Мы можем тестировать каждую функцию независимо, что делает обнаружение и устранение ошибок более эффективными. 5️⃣Разделение задач: функции позволяют разделять сложные задачи на меньшие подзадачи. Это делает разработку более систематической, позволяя решать проблемы последовательно и шаг за шагом.

💬Что из себя представляют Go workspaces? 🔸В версии Go 1.18 был введен новый режим workspace, который позволяет работать с несколькими модулями одновременно без необходимости редактирования go.mod файлов для каждого модуля. 🔸В каждом workspace, модули рассматриваются как основные модули при разрешении зависимостей. До введения этой функции, для добавления функции в один модуль и ее использования в другом модуле, требовалось либо публиковать изменения первого модуля, либо редактировать файл go.mod зависимого модуля с директивой replace для локальных, неопубликованных изменений модуля. 🔸Режим workspace в Go 1.18 решает ряд проблем, в частности, устраняет необходимость полагаться на локальные замены модулей. Вместо этого, все зависимости контролируются с использованием файла go.work в корневой директории вашего workspace. 🔸Файл go.work содержит директивы use и replace, которые переопределяют индивидуальные файлы go.mod, устраняя необходимость редактирования каждого файла go.mod вручную. 🔸Для создания workspace используется команда go work init с перечислением директорий модулей в качестве аргументов, разделенных пробелами. Workspace не обязан содержать модули, с которыми вы работаете. Команда init создает файл go.work, который перечисляет модули в workspace. Если вы запустите go work init без аргументов, команда создаст пустой workspace. 📌Основные преимущества: 1. Организация кода: workspaces позволяют группировать связанные модули Go в одном месте, что упрощает управление и организацию кода, особенно в больших проектах с множеством зависимостей и модулей. 2. Управление зависимостями: с помощью workspaces можно легко управлять зависимостями между модулями. Это упрощает обновление, замену или удаление зависимостей в ваших проектах. 3. Локальная разработка: workspaces облегчают локальную разработку, позволяя вам работать над множеством модулей одновременно без необходимости постоянно публиковать изменения в удаленные репозитории. 4. Совместимость с Go модулями: workspaces расширяют возможности Go модулей, добавляя поддержку для работы с несколькими модулями в рамках одного workspace, что делает управление модулями более гибким и удобным. 5. Улучшенный рабочий процесс: Go workspaces предлагают улучшенный рабочий процесс для разработчиков, делая процесс сборки и тестирования более простым и эффективным. 6. Простота и переносимость: создание workspace не требует дополнительных инструментов. Это также обеспечивает переносимость между различными окружениями разработки и командами. 👉 Подробнее

💬Какие операторы есть в языке Go? Назовите основные. 📌Арифметические: + : сложение - : вычитание * : умножение / : деление % : остаток от деления ++ : инкремент -- : декремент 📌Сравнительные: == : равно != : не равно < : меньше <= : меньше или равно > : больше >= : больше или равно 📌Логические: && : логическое И (AND) || : логическое ИЛИ (OR) ! : логическое НЕ (NOT) 📌Битовые: & : битовое И (AND) | : битовое ИЛИ (OR) ^ : битовое исключающее ИЛИ (XOR) &^ : битовая маска сброса (AND NOT) << : левый сдвиг >> : правый сдвиг 📌Операторы: = : присваивание +=, -=, *=, /=, %= : присваивание с операцией &=, |=, ^=, <<=, >>=, &^= : битовое присваивание с операцией 📌Прочие: & : оператор получения адреса * : оператор разыменования указателя <- : оператор канала (используется для отправки и получения данных через каналы) := : присваивание, используемое для объявления и инициализации переменной

💬Что из себя представляет middleware в контексте веб-разработки на Go? 📌Middleware представляет собой промежуточные обработчики, которые имеют возможность выполнять код, обрабатывать запросы и ответы в процессе выполнения HTTP-запросов и ответов перед тем, как они достигнут конечных обработчиков. • Middleware можно использовать для различных целей, таких как аутентификация, авторизация, логирование, обработка ошибок и многое другое. • В Go, middleware часто реализуется как функции, которые принимают и возвращают значения, соответствующие интерфейсу http.Handler или http.HandlerFunc. • Это позволяет создавать цепочки middleware, которые можно легко комбинировать и повторно использовать в различных частях приложения. • Например, может существовать middleware для аутентификации пользователей, которое проверяет наличие действительного токена в заголовке HTTP-запроса перед тем, как передать управление следующему обработчику в цепочке. Если токен отсутствует или недействителен, middleware может вернуть ответ с ошибкой, не передавая управление дальше. • В Go существует несколько популярных библиотек и фреймворков, которые предлагают встроенные или легко интегрируемые решения для создания и управления middleware, такие как Gorilla Mux, Gin или Echo. 📌Подробнее: 👉 Разработка REST-серверов на Go. Часть 5: Middleware 👉 HTTP Middleware 👉 Учимся разрабатывать REST API на Go на примере сокращателя ссылок

💬В чем разница между поверхностным и глубоким копированием в Go? 📌Поверхностное копирование (shallow copy) означает создание новой переменной или структуры, которая ссылается на те же данные в памяти, что и исходная переменная/структура. Поверхностное копирование применимо, когда мы хотим использовать одни и те же данные, но с разными именами или переменными, и нам не важно, чтобы изменения в одной переменной влияли на другую. original := []int{1, 2, 3} copied := original copied[0] = 99 fmt.Println(original) // [99 2 3] 📌Глубокое копирование (deep copy) подразумевает создание новой переменной или структуры, которая имеет собственные, полностью независимые копии данных, содержащихся в исходной переменной или структуре. Глубокое копирование может быть важно, например, при работе со сложными структурами данных, где изменение одного элемента может повлиять на целую структуру, а также при сериализации данных или создании копии структуры. import "github.com/mohae/deepcopy" original := []int{1, 2, 3} copied := deepcopy.Copy(original) copied[0] = 99 fmt.Println(original) // [1 2 3]