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

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

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

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

Наши каналы: https://t.me/proglibrary/9197
Download Telegram
💬Для чего в программах на 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
💬Как обрабатывать сигналы 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.

👉 Подробнее
👍13
💬 Что возвращает функция len() в Go, если ей передана строка в кодировке UTF-8?

🔸Функция len() в Go возвращает количество байтов в строке, а не количество рун (символов Unicode).

🔸Если строка закодирована в UTF-8, каждый символ может занимать от 1 до 4 байтов.

🔸Таким образом, если в строке UTF-8 присутствуют многобайтовые символы, функция len() вернет значение, большее, чем количество символов в строке.


import (
"fmt"
"unicode/utf8"
)

func main() {
s := "世界"
fmt.Println("Byte length:", len(s))
fmt.Println("Rune count:", utf8.RuneCountInString(s))
}


🔸Результат:
Byte length: 6
Rune count: 2
👍6
💬Какие риски возникают при использовании нескольких тегов полей в одной структуре?

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

📌Простой пример:


type Post struct {
Title string `json:"title" bson:"title"`
SubTitle string `json:"subtitle" bson:"subtitle"`
}


🔸В примере у структуры Post для каждого поля есть два тега поля: json и bson. Эти теги могут использоваться в разных целях, например, для отправки HTTP-ответов (используя json) и обработки демаршализации MongoDB (используя bson).

🔸При использовании подобных тегов слой HTTP-ответа (веб-сервер) и слой хранения (MongoDB) становятся тесно связанными. Если мы хотим изменить title на, например, shortTitle, нам нужно будет обновить и HTTP-ответ (что также может повлиять на клиентов, обрабатывающих ответ), и хранение в MongoDB.
👍1
🧑‍💻 Статьи для IT: как объяснять и распространять значимые идеи

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

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

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

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

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

// Плохо

func main() {
defer recover()

panicCode()
}

// Лучше


func handlePanic() {
if panicInfo := recover(); panicInfo != nil {
fmt.Println(panicInfo)
}
}

func main() {
defer handlePanic()

panicCode()
}


📌Кроме того:

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

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

🔸Откат транзакций: в операциях, которые должны быть атомарными, таких как обновления базы данных, recover может использоваться для обнаружения паники и выполнения отката транзакции, чтобы поддерживать целостность данных.

🔸Логирование и отладка: recover может использоваться для перехвата паники, логирования диагностической информации и затем повторного вызова паники, чтобы стандартный процесс обработки ошибок мог продолжить работу.
8👍1
💬Для чего в Go предназначена директива "//go:embed"?

📌Начиная с Go 1.16, директива //go:embed представляет собой специальный комментарий, который используется для встраивания файлов и директорий непосредственно в скомпилированный бинарный файл Go.

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

Простой пример:


package main

import (
"embed"
"io/fs"
"net/http"
)

//go:embed static/*
var staticFiles embed.FS

func main() {
// Используем встроенные файлы напрямую
http.Handle("/", http.FileServer(http.FS(staticFiles)))
http.ListenAndServe(":8080", nil)
}


Все файлы в директории static встраиваются в бинарный файл. Директива //go:embed должна быть расположена непосредственно перед объявлением переменной без пустых строк между комментарием и объявлением. Это позволяет использовать staticFiles как файловую систему внутри Go-кода.

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

👉 Подробнее
👍13🔥2