Библиотека Go для собеса | вопросы с собеседований
6.88K subscribers
225 photos
8 videos
1 file
440 links
Вопросы с собеседований по Go и ответы на них.

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

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

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

Наши каналы: https://t.iss.one/proglibrary/9197
Download Telegram
💬Каким будет результат при различных операциях (чтение, запись и закрытие) и состояниях канала?

Сегодняшний ответ будет таким👇
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Command.png
375.7 KB
💬Что из себя представляет паттерн «Команда» и как его реализовать на Go?

📌Команда — это поведенческий паттерн, позволяющий заворачивать запросы или простые операции в отдельные объекты.
Это позволяет откладывать выполнение команд, выстраивать их в очереди, а также хранить историю и делать отмену.

❗️В описании паттерна применяются общие понятия, такие как Класс, Объект, Абстрактный класс. Применимо к Go, это Пользовательский Тип, Значение этого Типа и Интерфейс. Также в Go вместо общепринятого наследования используется агрегирование и встраивание.

📌В паттерне Команда мы оперируем следующими понятиями:

• Command — запрос в виде объекта на выполнение
• Receiver — объект-получатель запроса, который будет обрабатывать нашу команду;
• Invoker — объект-инициатор запроса

📌Паттерн отделяет объект, инициирующий операцию, от объекта, который знает, как ее выполнить. Единственное, что должен знать инициатор, это как отправить команду.

📌Для реализации требуется:

1. Базовый абстрактный класс, описывающий интерфейс команды
2. Класс ConcreteCommand, реализующий команду
3. Класс Invoker, реализующий инициатора, записывающий команду и провоцирующий её выполнение
4. Класс Receiver, реализующий получателя и имеющий набор действий, которые команда может запрашивать

📌Invoker умеет складывать команды и инициировать их выполнение по какому-то событию. Обратившись к Invoker, можно отменить команду, пока она не выполнена.

📌ConcreteCommand содержит в себе запросы к Receiver, которые тот должен выполнять. В свою очередь Receiver содержит только набор действий (Actions), которые выполняются при обращении к ним из ConcreteCommand.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
💬В чем разница между поверхностным и глубоким копированием в 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]
👍9
💬Что из себя представляет 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 на примере сокращателя ссылок
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9
💬Какие операторы есть в языке Go? Назовите основные.

📌Арифметические:
+ : сложение
- : вычитание
* : умножение
/ : деление
% : остаток от деления
++ : инкремент
-- : декремент

📌Сравнительные:
== : равно
!= : не равно
< : меньше
<= : меньше или равно
> : больше
>= : больше или равно

📌Логические:
&& : логическое И (AND)
|| : логическое ИЛИ (OR)
! : логическое НЕ (NOT)

📌Битовые:
& : битовое И (AND)
| : битовое ИЛИ (OR)
^ : битовое исключающее ИЛИ (XOR)
&^ : битовая маска сброса (AND NOT)
<< : левый сдвиг
>> : правый сдвиг

📌Операторы:
= : присваивание
+=, -=, *=, /=, %= : присваивание с операцией
&=, |=, ^=, <<=, >>=, &^= : битовое присваивание с операцией

📌Прочие:
& : оператор получения адреса
* : оператор разыменования указателя
<- : оператор канала (используется для отправки и получения данных через каналы)
:= : присваивание, используемое для объявления и инициализации переменной
10👍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 не требует дополнительных инструментов. Это также обеспечивает переносимость между различными окружениями разработки и командами.

👉 Подробнее
👍11🔥3🤔1
💬Для чего в программах на Go используются функции?

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

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

📌В Go композиция достигается путем встраивания одной структуры в другую. Например, встраивание одной структуры в другую позволяет получить доступ к полям встроенной структуры, как если бы они были полями внешней структуры. Композиция в Go более гибкая и рекомендуется как альтернатива наследованию.

📌В нашем примере:

Определен базовый тип Engine с двумя полями (Power и Type) и методом Start.
Определен тип Car, в который встраивается Engine.
В функции main создается экземпляр типа Car, и через этот экземпляр вызывается метод Start встроенного типа Engine, а также обращаются к полям встроенного типа Engine.

📌Таким образом, композиция в Go позволяет объединять простые типы в более сложные структуры, сохраняя при этом простоту и ясность кода.
👍14🔥3🤔3
🧑‍💻 Статьи для IT: как объяснять и распространять значимые идеи

Напоминаем, что у нас есть бесплатный курс для всех, кто хочет научиться интересно писать — о программировании и в целом.

Что: семь модулей, посвященных написанию, редактированию, иллюстрированию и распространению публикаций.

Для кого: для авторов, копирайтеров и просто программистов, которые хотят научиться интересно рассказывать о своих проектах.

👉Материалы регулярно дополняются, обновляются и корректируются. А еще мы отвечаем на все учебные вопросы в комментариях курса.
💬Что будет при чтении/записи из закрытого и неинициализированного канала?

📌Чтение из закрытого канала: когда мы пытаемся читать из закрытого канала, происходит следующее:

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
// panic: send on closed channel

📌Чтение из неинициализированного (nil) канала будет заблокировано, как и запись в неициализированный (nil) канал.

☝️Важно отслеживать состояния каналов, инициализировать и закрывать их по мере необходимости, чтобы избежать паник и непредсказуемого поведения.
👍8😁21
💬Что такое паника (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 затем используется для захвата значения паники и предотвращения завершения программы.

👉 Подробнее
👍102
💬В чем разница между context.Background() и context.TODO()?

📌Разница между context.Background() и context.TODO() в основном заключается в их семантическом использовании.

👉context.Background() используется как стартовый контекст, а context.TODO() указывает на то, что контекст будет предоставлен позже, другими словами, когда неясно, какой контекст использовать, или он еще недоступен. С точки зрения функциональности они идентичны.
🔥13👍2
💬Что из себя представляет стабы (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 вызван с правильным аргументом.
👍51
💬Что из себя представляют структурные теги в 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`. Этот пакет предоставляет функции для работы с типами и значениями во время выполнения, что позволяет изучать и изменять значения, типы и теги структур во время выполнения.

👉 Подробнее
Please open Telegram to view this post
VIEW IN TELEGRAM
👍122
💬Что такое `json:,omitempty` в контексте структур Go?

🔹Если вы читали предыдущий пост, наверняка заметили упоминание аннотации `json:,omitempty`. Например:

type fruit struct {
Name string
Length int `json:,omitempty`

}

🔹`json:,omitempty` — это тег JSON для поля в структуре. Если при демаршаллинге данных JSON в структуру это конкретное поле пусто, оно будет игнорироваться. Без тега omitempty будет использоваться значение по умолчанию.

🔹В приведенном выше примере без тега omitempty поле длины будет заполнено значением int 0. Если тип пустого поля — строка, "" (пустая строка) будет значением по умолчанию. Аналогично, значение по умолчанию для логического типа — false, nil для указателя, интерфейса, среза и мапы.

👉 Подробнее
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🤔1
💬Какова цель функции init() в Go?

📌Функция init() в Go вызывается автоматически при инициализации пакета. В Go нет конструкторов в классическом понимании, как в некоторых других ЯП, но функция init() предлагает возможность выполнять необходимую начальную настройку.

📌Несколько ключевых моментов:

1. Автоматический вызов: функция init() вызывается автоматически перед вызовом main() и не требует явного вызова.

2. Использование: функции init() можно использовать для инициализации глобальных переменных, проверки или установки конфигурации, установки соединений с базами данных и других целей.

3. Несколько функций init(): в одном пакете можно иметь несколько функций init(). Они будут вызваны в том порядке, в котором объявлены в файле.

4. В случае зависимостей между пакетами, функции init() из импортированных пакетов выполняются перед функцией init() из основного пакета.

👉 Подробнее
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
💬Как переобъявить переменные с помощью коротких объявлений?

📌В одной области видимости нельзя переобъявлять переменные, но это можно делать в объявлении нескольких переменных (multi-variable declarations), среди которых хотя бы одна — новая. Переобъявляемые переменные должны располагаться в том же блоке, иначе получится затенённая переменная.

📌Неправильно:

func main() {
one := 0
one := 1
// ошибка компиляции
}


📌Правильно:

func main() {
one := 0
one, two := 1,2

one,two = two,one
}
👍10
💬В чем разница между пакетами и модулями Go?

📌В Go, пакет — это коллекция исходных файлов .go в одной директории и с одинаковой директивой package, в то время как модуль — это дерево пакетов. Имя модуля задаётся в go.mod.

📌Файлы для одного пакета должны находиться в одной директории. Полное имя для пакета строится из имени модуля и пути к директории с файлами.

◆ Например, в go.mod указано module example.org/mylib, тогда все пакеты из модуля example.org/mylib должны быть в дочерних директориях относительно go.mod, и путь к директории определяет имя пакета.

◆ Например, в дереве исходников нашей библиотеки есть файлы в директоории ./cmd/root. Тогда эти файлы должны быть либо с директивой package root, либо package root_test. И полное имя пакета для этих файлов будет либо example.org/mylib/cmd/root, либо example.org/mylib/cmd/root_test (тесты для пакета example.org/mylib/cmd/root).
👍12
💬Предположим, что мы хотим выполнить код Go в какой-то момент в будущем или повторно через определенный интервал. Что необходимо использовать в таком случае?

📌Встроенные функции timer и ticker упрощают обе задачи, при этом таймеры предназначены для кейсов, когда мы хотим сделать что-то один раз в будущем, а тикеры — для кейсов, когда мы хотим сделать что-то повторно через определенные промежутки времени.

🔹Таймеры (первый пример):

◆ Таймеры представляют собой способ ожидания определенного времени перед выполнением действия.
◆ Создание таймера выполняется с помощью функции time.NewTimer(); в нее передаем длительность времени, которую необходимо ожидать.
◆ В примере таймер имеет канал C, в который будет отправлено значение после истечения заданного времени.
◆ Также возможно остановить таймер перед его активацией с помощью метода Stop().

🔹Тикеры (второй пример):

◆ Тикеры используются для выполнения действий через регулярные промежутки времени.
◆ Создание тикера также выполняется с помощью функции в пакете time, в данном случае time.NewTicker(), с указанием интервала между «тиками».
◆ Аналогично таймерам, тикеры имеют канал C, в который отправляется значение на каждом «тике».
◆ Тикеры можно остановить, используя метод Stop(), что предотвратит дальнейшее отправление значений.
👍9🥱4