200 вопросов по Go
200 вопросов по Go
Основы языка Go
Вопрос
Что такое Go?
Ответ
Go — это статически типизированный, компилируемый язык программирования, разработанный компанией Google. Он ориентирован на простоту, производительность, безопасность памяти и встроенную поддержку конкурентности через горутины и каналы.
Вопрос
Как скомпилировать и запустить программу на Go?
Ответ
Программу на Go можно скомпилировать и запустить одной командой:
go run main.go
Для получения исполняемого файла используется команда:
go build main.go
Результатом будет бинарный файл main (или main.exe на Windows).
Вопрос
Из каких обязательных элементов состоит минимальная программа на Go?
Ответ
Минимальная программа на Go содержит:
- объявление пакета
main; - импорт пакета
fmt(если используются функции ввода-вывода); - функцию
main, которая является точкой входа.
Пример:
package main
func main() {
}
Вопрос
Что такое пакет в Go?
Ответ
Пакет в Go — это коллекция исходных файлов, расположенных в одной директории и объявляющих один и тот же пакет с помощью ключевого слова package. Пакеты служат для организации кода, инкапсуляции и повторного использования.
Вопрос
Как импортировать пакет в Go?
Ответ
Пакет импортируется с помощью ключевого слова import. Например:
import "fmt"
Можно импортировать несколько пакетов:
import (
"fmt"
"os"
)
Вопрос
Как объявить переменную в Go?
Ответ
Переменную можно объявить несколькими способами:
- с явным указанием типа:
var name string - с инициализацией и выводом типа:
var age = 25 - с коротким объявлением внутри функции:
name := "Alice"
Вопрос
Что такое короткое объявление переменной?
Ответ
Короткое объявление переменной использует оператор := и доступно только внутри функций. Оно одновременно объявляет и инициализирует переменную, а тип выводится из значения справа. Пример:
x := 42 // x имеет тип int
Вопрос
Можно ли использовать короткое объявление на уровне пакета?
Ответ
Нет. Короткое объявление (:=) допустимо только внутри блоков функций. На уровне пакета необходимо использовать var.
Вопрос
Какие базовые типы данных есть в Go?
Ответ
Базовые типы данных в Go делятся на следующие категории:
- Числовые:
int,int8,int16,int32,int64,uint,uint8, ...,float32,float64,complex64,complex128 - Строковые:
string - Логические:
bool
Вопрос
Чем отличается int от int32 и int64?
Ответ
Тип int имеет размер, зависящий от архитектуры системы (обычно 32 или 64 бита). Типы int32 и int64 имеют фиксированный размер — 32 и 64 бита соответственно. Использование int предпочтительно для индексов и счётчиков, а фиксированные типы — когда важна переносимость или соответствие внешним форматам.
Вопрос
Что такое нулевое значение в Go?
Ответ
Нулевое значение — это значение, присваиваемое переменной при её объявлении без инициализации. Для числовых типов это 0, для bool — false, для строк — пустая строка "", для указателей, срезов, карт, каналов и функций — nil.
Вопрос
Как преобразовать тип одного значения в другой?
Ответ
Преобразование типов в Go выполняется с помощью синтаксиса T(x), где T — целевой тип, а x — исходное значение. Пример:
var i int = 42
var f float64 = float64(i)
Go не поддерживает неявные преобразования между числовыми типами.
Вопрос
Что такое константа в Go?
Ответ
Константа — это значение, которое определяется во время компиляции и не может быть изменено во время выполнения. Объявляется с помощью ключевого слова const. Пример:
const Pi = 3.14159
Вопрос
Можно ли использовать := для объявления константы?
Ответ
Нет. Константы объявляются только с помощью const. Оператор := предназначен исключительно для переменных.
Вопрос
Что такое iota?
Ответ
iota — это встроенная константа, используемая внутри блоков const. Она начинается с 0 и увеличивается на 1 для каждой последующей строки в блоке. Пример:
const (
A = iota // 0
B // 1
C // 2
)
Вопрос
Как объявить функцию в Go?
Ответ
Функция объявляется с помощью ключевого слова func, за которым следует имя, список параметров в скобках и, при необходимости, список возвращаемых значений. Пример:
func add(a int, b int) int {
return a + b
}
Вопрос
Можно ли опустить тип у параметров, если они идут подряд и имеют одинаковый тип?
Ответ
Да. Если несколько последовательных параметров имеют одинаковый тип, его можно указать один раз в конце списка. Пример:
func add(a, b int) int {
return a + b
}
Вопрос
Поддерживает ли Go функции с несколькими возвращаемыми значениями?
Ответ
Да. Функция может возвращать любое количество значений. Пример:
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
Вопрос
Что такое именованные возвращаемые значения?
Ответ
Именованные возвращаемые значения — это параметры возврата, которым присвоены имена. Они инициализируются нулевыми значениями и могут использоваться как обычные переменные внутри функции. Пример:
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return // "naked return"
}
Вопрос
Что такое "голый возврат" (naked return)?
Ответ
"Голый возврат" — это оператор return без указания значений в функции с именованными возвращаемыми параметрами. Он возвращает текущие значения именованных переменных. Такой стиль рекомендуется использовать только в коротких функциях.
Вопрос
Как работает условный оператор if в Go?
Ответ
Оператор if в Go не требует круглых скобок вокруг условия, но фигурные скобки обязательны. Условие должно быть выражением типа bool. Пример:
if x > 0 {
fmt.Println("positive")
}
Вопрос
Можно ли объявить переменную в условии if?
Ответ
Да. Перед условием можно объявить переменную, которая будет доступна в блоках if и else. Пример:
if v := math.Pow(x, 2); v < 10 {
fmt.Println(v)
} else {
fmt.Println("too big")
}
Вопрос
Как работает цикл for в Go?
Ответ
В Go существует только один цикл — for. Он может использоваться в трёх формах:
- классическая:
for i := 0; i < 10; i++ - как
while:for condition - бесконечный:
for
Пример:
for i := 0; i < 5; i++ {
fmt.Println(i)
}
Вопрос
Есть ли в Go цикл while?
Ответ
Цикла while как отдельной конструкции нет. Его заменяет форма for с одним условием:
for condition {
// тело цикла
}
Вопрос
Как выйти из цикла или пропустить итерацию?
Ответ
Для выхода из цикла используется break, для перехода к следующей итерации — continue.
Вопрос
Поддерживает ли Go метки (labels) для циклов?
Ответ
Да. Метки позволяют управлять вложенными циклами. Пример:
Loop:
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if i == 1 && j == 1 {
break Loop
}
}
}
Вопрос
Как работает оператор switch в Go?
Ответ
Оператор switch сравнивает выражение с каждым case. Он не требует break в конце каждого случая — выполнение автоматически прекращается после первого совпадения. Пример:
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("macOS")
case "linux":
fmt.Println("Linux")
default:
fmt.Printf("%s\n", os)
}
Вопрос
Можно ли использовать условия в case оператора switch?
Ответ
Да. Если после switch не указано выражение, каждый case может содержать логическое условие. Такой switch эквивалентен цепочке if-else. Пример:
switch {
case x < 0:
fmt.Println("negative")
case x == 0:
fmt.Println("zero")
default:
fmt.Println("positive")
}
Вопрос
Что такое defer?
Ответ
defer откладывает выполнение функции до выхода из окружающей функции. Отложенные вызовы выполняются в порядке LIFO (последним пришёл — первым ушёл). Пример:
func readFile(filename string) {
file, _ := os.Open(filename)
defer file.Close()
// работа с файлом
}
Вопрос
Когда полезно использовать defer?
Ответ
defer полезен для освобождения ресурсов: закрытия файлов, разблокировки мьютексов, завершения транзакций и других действий, которые должны быть выполнены гарантированно перед выходом из функции.
Вопрос
Как передаются аргументы в функцию — по значению или по ссылке?
Ответ
Аргументы всегда передаются по значению. Это означает, что функция получает копию аргумента. Изменения внутри функции не влияют на оригинальное значение, если только не передаётся указатель.
Вопрос
Как получить адрес переменной?
Ответ
Адрес переменной получается с помощью оператора &. Пример:
x := 42
p := &x // p — указатель на x
Вопрос
Как разыменовать указатель?
Ответ
Разыменование указателя выполняется с помощью оператора *. Пример:
p := &x
*p = 100 // изменяет значение x
Вопрос
Можно ли создать указатель на литерал?
Ответ
Нет. Указатель можно получить только от переменной. Однако стандартная библиотека предоставляет вспомогательные функции, например new(int) или ptr.To(value) (в Go 1.22+).
Вопрос
Что такое nil?
Ответ
nil — это нулевое значение для указателей, срезов, карт, каналов, интерфейсов и функций. Оно означает «ничего» или «не инициализировано».
Вопрос
Можно ли сравнить два указателя?
Ответ
Да. Указатели можно сравнивать с помощью операторов == и !=. Сравнение показывает, ссылаются ли они на один и тот же адрес.
Вопрос
Что такое область видимости переменной в Go?
Ответ
Область видимости определяется блоком, в котором объявлена переменная. Переменная, объявленная внутри блока (например, внутри if или цикла), недоступна вне этого блока. Имена, начинающиеся с заглавной буквы, экспортируются из пакета.
Вопрос
Какие имена экспортируются из пакета?
Ответ
Имена (переменных, функций, типов и т.д.), начинающиеся с заглавной буквы, являются экспортируемыми и доступны другим пакетам. Имена со строчной буквы — внутренние для пакета.
Составные типы данных
Вопрос
Что такое массив в Go?
Ответ
Массив в Go — это упорядоченная коллекция фиксированного размера, состоящая из элементов одного типа. Размер массива является частью его типа, поэтому массивы разных размеров считаются разными типами. Пример объявления:
var a [3]int
Вопрос
Можно ли изменить размер массива после его создания?
Ответ
Нет. Размер массива фиксирован на этапе компиляции и не может быть изменён во время выполнения.
Вопрос
Как инициализировать массив при объявлении?
Ответ
Массив можно инициализировать литералом:
a := [3]int{1, 2, 3}
Или с автоматическим определением длины:
a := [...]int{1, 2, 3} // компилятор выведет длину как 3
Вопрос
Что такое срез (slice) в Go?
Ответ
Срез — это динамическая структура данных, представляющая собой «окно» в массив. Он содержит указатель на массив, длину и вместимость. В отличие от массива, срез может менять размер во время выполнения.
Вопрос
Как создать срез?
Ответ
Срез можно создать несколькими способами:
- через литерал:
s := []int{1, 2, 3} - с помощью функции
make:s := make([]int, 5)— создаёт срез длиной 5 и вместимостью 5 - путём взятия подмассива:
s := arr[1:4]
Вопрос
В чём разница между длиной и вместимостью среза?
Ответ
Длина (len) — это количество элементов, доступных в срезе. Вместимость (cap) — это максимальное количество элементов, которое срез может содержать без реаллокации, то есть размер базового массива от начала среза до его конца.
Вопрос
Что происходит при добавлении элемента в срез с помощью append?
Ответ
Функция append добавляет элемент(ы) в конец среза. Если текущая вместимость недостаточна, Go автоматически выделяет новый базовый массив большего размера, копирует туда старые элементы и добавляет новые. Исходный срез остаётся неизменным; append возвращает новый срез.
Вопрос
Почему важно передавать указатель на срез, если нужно изменить его длину внутри функции?
Ответ
Потому что срез — это структура из трёх полей (указатель, длина, вместимость), передаваемая по значению. Изменение длины или вместимости внутри функции затрагивает только локальную копию. Чтобы изменения были видны снаружи, нужно либо возвращать новый срез, либо принимать указатель на срез.
Вопрос
Как скопировать один срез в другой?
Ответ
Для копирования используется встроенная функция copy:
src := []int{1, 2, 3}
dst := make([]int, len(src))
n := copy(dst, src) // n = 3
Функция копирует минимальное количество элементов из двух срезов и возвращает число скопированных элементов.
Вопрос
Что такое карта (map) в Go?
Ответ
Карта — это встроенная ассоциативная структура данных, хранящая пары «ключ–значение». Ключи должны быть сравнимыми типами (например, string, int, структуры без срезов/карт). Карта неупорядочена.
Вопрос
Как создать карту?
Ответ
Карту можно создать так:
- через литерал:
m := map[string]int{"one": 1, "two": 2} - с помощью
make:m := make(map[string]int) - объявление без инициализации:
var m map[string]int— такойmравенnilи не готов к использованию
Вопрос
Что произойдёт, если обратиться к несуществующему ключу в карте?
Ответ
Будет возвращено нулевое значение для типа значения. Например, для map[string]int — это 0. Чтобы проверить наличие ключа, используется форма с двумя возвращаемыми значениями:
value, exists := m["key"]
Вопрос
Можно ли использовать срез в качестве ключа карты?
Ответ
Нет. Ключи карты должны быть сравнимыми, а срезы не поддерживают оператор ==. Использование среза в качестве ключа вызовет ошибку компиляции.
Вопрос
Как перебрать все элементы карты?
Ответ
С помощью цикла for range:
for key, value := range m {
fmt.Println(key, value)
}
Порядок итерации не гарантируется и может отличаться между запусками программы.
Вопрос
Что такое структура (struct) в Go?
Ответ
Структура — это составной тип, объединяющий несколько полей с именами и типами. Она позволяет группировать связанные данные. Пример:
type Person struct {
Name string
Age int
}
Вопрос
Как создать экземпляр структуры?
Ответ
Экземпляр можно создать так:
- через литерал:
p := Person{Name: "Alice", Age: 30} - через переменную:
var p Person— все поля получат нулевые значения - через указатель:
p := &Person{Name: "Bob"}
Вопрос
Можно ли опускать имена полей при инициализации структуры?
Ответ
Да, но только если указывать значения в том же порядке, в котором объявлены поля, и только для структур, определённых в том же пакете. Однако такой стиль считается менее читаемым и не рекомендуется.
Вопрос
Что такое встраивание (embedding) в Go?
Ответ
Встраивание — это механизм композиции, при котором один тип включает другой тип без явного имени поля. Поля и методы встраиваемого типа становятся доступны напрямую. Пример:
type Address struct { City string }
type Person struct {
Name string
Address // встраивание
}
// Теперь p.City допустимо
Вопрос
Поддерживает ли Go наследование?
Ответ
Go не поддерживает классическое наследование. Вместо него используется композиция через встраивание и интерфейсы для полиморфизма.
Вопрос
Что такое теги (tags) в структурах?
Ответ
Теги — это строковые метаданные, прикреплённые к полям структуры в обратных кавычках. Они используются рефлексией для сериализации, валидации и других задач. Пример:
type User struct {
Name string `json:"name" validate:"required"`
}
Вопрос
Можно ли сравнивать две структуры?
Ответ
Да, если все их поля являются сравнимыми. Сравнение выполняется по всем полям. Структуры с полями-срезами, картами или функциями несравнимы.
Вопрос
Что произойдёт при присваивании одной структуры другой?
Ответ
Произойдёт глубокое копирование всех полей. Поскольку структуры передаются по значению, изменения в одной копии не влияют на другую.
Вопрос
Как получить размер структуры в байтах?
Ответ
С помощью функции unsafe.Sizeof:
import "unsafe"
size := unsafe.Sizeof(Person{})
Это значение зависит от выравнивания полей и архитектуры.
Вопрос
Что такое анонимная структура?
Ответ
Анонимная структура — это структура, объявленная без имени типа. Она часто используется для временных данных или JSON-парсинга:
data := struct {
ID int `json:"id"`
Name string `json:"name"`
}{ID: 1, Name: "Test"}
Функции, методы, интерфейсы и ошибки
Вопрос
Что такое метод в Go?
Ответ
Метод — это функция, привязанная к определённому типу через получатель (receiver). Получатель указывается перед именем метода. Пример:
func (p Person) Greet() string {
return "Hello, " + p.Name
}
Вопрос
Может ли получатель быть указателем?
Ответ
Да. Получатель может быть как значением, так и указателем. Использование указателя позволяет изменять поля структуры внутри метода и избегать копирования больших структур. Пример:
func (p *Person) SetAge(age int) {
p.Age = age
}
Вопрос
К каким типам можно привязывать методы?
Ответ
Методы можно определять только для типов, объявленных в том же пакете. Нельзя добавлять методы к встроенным типам напрямую (например, int или string), но можно создать алиас:
type MyInt int
func (m MyInt) Double() MyInt { return m * 2 }
Вопрос
Что такое интерфейс в Go?
Ответ
Интерфейс — это тип, описывающий набор методов. Любой тип, реализующий все методы интерфейса, автоматически считается его реализацией. Это механизм утиной типизации. Пример:
type Speaker interface {
Speak() string
}
Вопрос
Нужно ли явно указывать, что тип реализует интерфейс?
Ответ
Нет. Реализация интерфейса в Go неявна. Достаточно, чтобы у типа были все методы, описанные в интерфейсе.
Вопрос
Что такое пустой интерфейс?
Ответ
Пустой интерфейс interface{} (в Go 1.18+ также записывается как any) — это интерфейс без методов. Его реализует любой тип. Он используется для хранения значений любого типа, например, в картах или функциях общего назначения.
Вопрос
Как проверить, реализует ли значение конкретный тип?
Ответ
С помощью утверждения типа (type assertion):
value, ok := someInterface.(ConcreteType)
Если ok равно true, преобразование успешно.
Вопрос
Как безопасно работать с утверждением типа?
Ответ
Всегда использовать форму с двумя возвращаемыми значениями (value, ok), чтобы избежать паники при несоответствии типов.
Вопрос
Что такое переключатель по типу (type switch)?
Ответ
Это конструкция switch для интерфейсов, позволяющая выполнять разные действия в зависимости от конкретного типа значения. Пример:
switch v := i.(type) {
case int:
fmt.Println("int:", v)
case string:
fmt.Println("string:", v)
default:
fmt.Println("unknown")
}
Вопрос
Как объявить интерфейс с одним методом?
Ответ
Пример интерфейса с одним методом:
type Closer interface {
Close() error
}
Такие интерфейсы широко используются в стандартной библиотеке (например, io.Reader, io.Writer).
Вопрос
Почему в Go предпочитают маленькие интерфейсы?
Ответ
Маленькие интерфейсы (часто с одним методом) повышают гибкость и повторное использование кода. Они легко реализуются и комбинируются, что соответствует принципу «принимай интерфейсы, возвращай структуры».
Вопрос
Что такое встраивание интерфейсов?
Ответ
Один интерфейс может встраивать другой, наследуя его методы. Пример:
type ReadWriter interface {
Reader // встраивает io.Reader
Writer // встраивает io.Writer
}
Вопрос
Как обрабатываются ошибки в Go?
Ответ
Ошибки в Go представлены встроенным типом error. Функции, которые могут завершиться неудачно, возвращают error как последнее значение. Вызывающий код проверяет его на nil. Пример:
if err != nil {
log.Fatal(err)
}
Вопрос
Что такое error на самом деле?
Ответ
error — это встроенный интерфейс:
type error interface {
Error() string
}
Любой тип, реализующий метод Error() string, удовлетворяет этому интерфейсу.
Вопрос
Как создать простую ошибку?
Ответ
С помощью функции errors.New:
err := errors.New("something went wrong")
Или с форматированием:
err := fmt.Errorf("invalid input: %s", input)
Вопрос
Поддерживает ли Go обработку исключений (exceptions)?
Ответ
Go не использует исключения. Вместо этого применяется явная проверка ошибок через возвращаемые значения. Это делает поток управления более предсказуемым.
Вопрос
Что такое panic?
Ответ
panic — это встроенная функция, которая немедленно прекращает нормальное выполнение функции и начинает раскрутку стека (panicking). Она предназначена для фатальных ошибок, которые невозможно корректно обработать.
Вопрос
Как остановить раскрутку стека после panic?
Ответ
С помощью встроенной функции recover, вызываемой внутри отложенной функции (defer). Пример:
func safeCall() (ok bool) {
defer func() {
if r := recover(); r != nil {
ok = false
}
}()
riskyFunction()
return true
}
Вопрос
Когда следует использовать panic?
Ответ
Только в ситуациях, когда продолжение выполнения программы невозможно или приведёт к повреждению данных — например, при неудачной инициализации критического ресурса. В обычном коде предпочтительна явная обработка ошибок.
Вопрос
Что такое обёртки ошибок (error wrapping)?
Ответ
Обёртка ошибки — это новая ошибка, содержащая другую ошибку как причину. Это позволяет сохранять контекст. С Go 1.13 поддерживается синтаксис %w в fmt.Errorf:
return fmt.Errorf("failed to open file: %w", err)
Вопрос
Как проверить, содержит ли ошибка определённую причину?
Ответ
С помощью функции errors.Is для сравнения с конкретной ошибкой и errors.As для извлечения ошибки определённого типа:
if errors.Is(err, os.ErrNotExist) {
// файл не существует
}
var pathError *os.PathError
if errors.As(err, &pathError) {
// извлечена структура PathError
}
Вопрос
Можно ли сравнивать ошибки с помощью ==?
Ответ
Да, но только если они созданы как глобальные переменные (например, io.EOF). Для обёрнутых ошибок или динамически созданных экземпляров нужно использовать errors.Is.
Вопрос
Что такое функция высшего порядка в Go?
Ответ
Функция высшего порядка — это функция, которая принимает другую функцию в качестве аргумента или возвращает функцию. Пример:
func apply(f func(int) int, x int) int {
return f(x)
}
Вопрос
Поддерживает ли Go замыкания?
Ответ
Да. Функция, определённая внутри другой функции, может захватывать переменные из окружающей области видимости. Эти переменные сохраняются даже после выхода из внешней функции.
Вопрос
Что такое анонимная функция?
Ответ
Анонимная функция — это функция без имени, которую можно определить и вызвать на месте. Часто используется в defer, go или как коллбэк. Пример:
func() {
fmt.Println("anonymous")
}()
Конкурентность
Вопрос
Что такое горутина?
Ответ
Горутина — это легковесная нить выполнения, управляемая планировщиком Go, а не операционной системой. Горутины запускаются с помощью ключевого слова go перед вызовом функции. Они потребляют мало памяти (начинаются с ~2 КБ стека) и могут масштабироваться до сотен тысяч экземпляров.
Вопрос
Как запустить функцию в горутине?
Ответ
Перед вызовом функции добавляется ключевое слово go:
go myFunction()
go func() {
fmt.Println("anonymous goroutine")
}()
Вопрос
Когда завершается программа, если в ней запущены горутины?
Ответ
Программа завершается немедленно после выхода из функции main, даже если другие горутины всё ещё работают. Чтобы дождаться их завершения, используются примитивы синхронизации: sync.WaitGroup, каналы или time.Sleep (не рекомендуется в production).
Вопрос
Что такое канал (channel) в Go?
Ответ
Канал — это тип, предназначенный для безопасной передачи данных между горутинами. Он обеспечивает синхронизацию и предотвращает гонки данных. Каналы создаются с помощью встроенной функции make.
Вопрос
Как создать канал?
Ответ
Буферизованный и небуферизованный каналы создаются так:
ch := make(chan int) // небуферизованный
bufCh := make(chan int, 3) // буферизованный, вместимость 3
Вопрос
В чём разница между буферизованным и небуферизованным каналом?
Ответ
Небуферизованный канал блокирует отправителя до тех пор, пока другой поток не выполнит приём. Буферизованный канал блокирует отправителя только когда буфер заполнен, а получателя — когда буфер пуст.
Вопрос
Как отправить и принять значение через канал?
Ответ
Отправка: ch <- value
Приём: value := <-ch
Приём без сохранения: <-ch
Вопрос
Можно ли закрыть канал?
Ответ
Да. Канал закрывается с помощью встроенной функции close(ch). После закрытия нельзя отправлять данные, но можно получать оставшиеся. При попытке чтения из закрытого канала возвращается нулевое значение и флаг ok == false.
Вопрос
Кто должен закрывать канал?
Ответ
Тот, кто отвечает за его жизненный цикл — обычно отправитель. Получатель не должен закрывать канал, особенно если он используется несколькими отправителями.
Вопрос
Что такое операция select?
Ответ
select — это конструкция, позволяющая ждать на нескольких операциях с каналами одновременно. Она выбирает случайный готовый case или выполняет default, если указан и ни один канал не готов. Пример:
select {
case msg := <-ch1:
fmt.Println(msg)
case ch2 <- "hello":
fmt.Println("sent")
default:
fmt.Println("no activity")
}
Вопрос
Что произойдёт, если все каналы в select заблокированы и нет default?
Ответ
Горутина заблокируется до тех пор, пока один из каналов не станет готов к операции.
Вопрос
Как реализовать таймаут при работе с каналами?
Ответ
С помощью канала time.After:
select {
case res := <-ch:
fmt.Println(res)
case <-time.After(1 * time.Second):
fmt.Println("timeout")
}
Вопрос
Что такое направление канала?
Ответ
Канал может быть объявлен как только для отправки (chan<- T) или только для приёма (<-chan T). Это позволяет усилить типобезопасность интерфейсов функций. Пример:
func sender(ch chan<- int) { ch <- 42 }
func receiver(ch <-chan int) { fmt.Println(<-ch) }
Вопрос
Можно ли преобразовать двунаправленный канал в однонаправленный?
Ответ
Да. Неявное преобразование происходит автоматически при передаче в функцию с соответствующим типом параметра. Обратное преобразование невозможно.
Вопрос
Что такое паника при отправке в закрытый канал?
Ответ
Попытка отправить значение в закрытый канал вызывает панику. Чтение из закрытого канала безопасно и возвращает нулевое значение.
Вопрос
Как дождаться завершения нескольких горутин?
Ответ
С помощью sync.WaitGroup:
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
wg.Add(1)
go func() {
defer wg.Done()
// работа
}()
}
wg.Wait()
Вопрос
Что такое sync.Mutex?
Ответ
sync.Mutex — это мьютекс, примитив синхронизации, обеспечивающий взаимное исключение. Он предотвращает одновременный доступ нескольких горутин к общему ресурсу. Используется методами Lock() и Unlock().
Вопрос
Как правильно использовать Mutex?
Ответ
Следует всегда разблокировать мьютекс после блокировки, обычно с помощью defer:
mu.Lock()
defer mu.Unlock()
// критическая секция
Вопрос
Что такое sync.RWMutex?
Ответ
RWMutex (readers-writer mutex) позволяет нескольким читателям одновременно получать доступ к ресурсу, но только одному писателю. Использует методы RLock()/RUnlock() для чтения и Lock()/Unlock() для записи.
Вопрос
Что такое гонка данных (data race)?
Ответ
Гонка данных возникает, когда две или более горутин одновременно обращаются к одной переменной, и хотя бы одна из них записывает в неё, без синхронизации. Это приводит к неопределённому поведению.
Вопрос
Как обнаружить гонки данных?
Ответ
С помощью флага -race при сборке или запуске:
go run -race main.go
go test -race ./...
Вопрос
Что такое пул горутин (worker pool)?
Ответ
Пул горутин — это паттерн, при котором фиксированное число рабочих горутин обрабатывает задачи из общего канала-очереди. Это ограничивает параллелизм и предотвращает перегрузку системы.
Вопрос
Как реализовать простой пул горутин?
Ответ
Пример пула из трёх воркеров:
jobs := make(chan int, 100)
results := make(chan int, 100)
for w := 0; w < 3; w++ {
go func() {
for j := range jobs {
results <- j * 2
}
}()
}
// отправка задач
for j := 1; j <= 5; j++ {
jobs <- j
}
close(jobs)
// сбор результатов
for a := 1; a <= 5; a++ {
<-results
}
Вопрос
Что такое context в Go?
Ответ
context.Context — это тип из пакета context, используемый для передачи отмены, дедлайнов и значений между горутинами. Он помогает контролировать жизненный цикл операций.
Вопрос
Как создать контекст с таймаутом?
Ответ
С помощью функции context.WithTimeout:
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
Вопрос
Как передать сигнал отмены в горутину?
Ответ
Горутина периодически проверяет ctx.Done():
select {
case <-ctx.Done():
return ctx.Err()
default:
// продолжить работу
}
Вопрос
Можно ли использовать глобальные переменные в конкурентном коде?
Ответ
Только если доступ к ним синхронизирован с помощью мьютексов, атомарных операций или каналов. Без синхронизации использование глобальных переменных приводит к гонкам данных.
Вопрос
Что такое атомарные операции в Go?
Ответ
Атомарные операции — это неразрывные действия над целыми числами и указателями, предоставляемые пакетом sync/atomic. Они безопасны для использования из нескольких горутин без мьютексов. Пример:
var counter int64
atomic.AddInt64(&counter, 1)
value := atomic.LoadInt64(&counter)
Модули, пакеты и инструменты
Вопрос
Что такое модуль в Go?
Ответ
Модуль — это коллекция связанных пакетов, распространяемых вместе и управляемых как единое целое. Модуль определяется файлом go.mod в корне проекта и имеет уникальный путь (module path).
Вопрос
Как инициализировать новый модуль?
Ответ
С помощью команды:
go mod init example.com/myproject
Это создаёт файл go.mod с указанием пути модуля и версии Go.
Вопрос
Что содержится в файле go.mod?
Ответ
Файл go.mod содержит:
- директиву
moduleс путём модуля; - требуемую версию Go (
go 1.22); - список прямых зависимостей (
require); - исключения (
exclude) или замены (replace), если используются.
Пример:
module example.com/app
go 1.22
require (
github.com/some/lib v1.2.3
)
Вопрос
Что такое go.sum?
Ответ
go.sum — это файл с криптографическими хешами всех скачанных зависимостей. Он гарантирует воспроизводимость сборки и защиту от подмены содержимого пакетов.
Вопрос
Как добавить новую зависимость?
Ответ
Просто используйте её в коде и выполните:
go mod tidy
Или явно:
go get github.com/user/repo@v1.0.0
Вопрос
Что делает команда go mod tidy?
Ответ
Она синхронизирует go.mod и go.sum: удаляет неиспользуемые зависимости и добавляет недостающие, основываясь на импортах в коде.
Вопрос
Как обновить зависимости?
Ответ
Для обновления всех прямых и транзитивных зависимостей:
go get -u ./...
Для обновления только прямых:
go get -u
Вопрос
Что такое семантическое импортирование в Go?
Ответ
Go следует правилам семантического версионирования (SemVer). Версии v0.x.y и v1.x.y импортируются напрямую. Начиная с v2+, путь модуля должен содержать суффикс /v2, /v3 и т.д., чтобы обеспечить обратную совместимость.
Вопрос
Как организовать внутренние пакеты?
Ответ
Пакеты, помещённые в директорию internal, доступны только для кода внутри того же дерева модуля. Это механизм инкапсуляции на уровне файловой системы.
Вопрос
Что такое пакет main?
Ответ
Пакет main — это исполняемый пакет, содержащий функцию main. Он компилируется в бинарный файл, а не в библиотеку.
Вопрос
Можно ли импортировать пакет main?
Ответ
Нет. Пакет main предназначен только для сборки исполняемых файлов и не может быть импортирован другими пакетами.
Вопрос
Какие инструменты входят в стандартный набор Go?
Ответ
Стандартный набор включает:
go build— компиляция;go run— запуск без сохранения бинарника;go test— запуск тестов;go fmt— форматирование кода;go vet— статический анализ;go doc— генерация документации;go mod— управление модулями.
Вопрос
Как отформатировать весь проект?
Ответ
Командой:
go fmt ./...
Вопрос
Что проверяет go vet?
Ответ
go vet выявляет подозрительные конструкции: неиспользуемые переменные в форматах, неправильные вызовы fmt.Printf, синхронизацию и другие потенциальные ошибки.
Вопрос
Как запустить все тесты в проекте?
Ответ
Командой:
go test ./...
Вопрос
Как измерить покрытие кода тестами?
Ответ
С помощью флага -cover:
go test -cover ./...
Для детального отчёта:
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
Вопрос
Что такое benchmark в Go?
Ответ
Benchmark — это функция, начинающаяся с Benchmark, используемая для измерения производительности. Запускается командой:
go test -bench=.
Пример:
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
add(1, 2)
}
}
Вопрос
Как профилировать программу на Go?
Ответ
С помощью пакета net/http/pprof или runtime/pprof. Для HTTP-сервера достаточно импортировать _ "net/http/pprof", после чего эндпоинты /debug/pprof становятся доступны. Можно собирать профили CPU, памяти, блокировок и горутин.
Вопрос
Как уменьшить аллокации в горутинах?
Ответ
Использовать пулы объектов (sync.Pool), избегать захвата переменных в замыканиях, переиспользовать буферы и минимизировать создание временных структур.
Вопрос
Что такое escape analysis?
Ответ
Escape analysis — это анализ компилятором, определяющий, может ли переменная быть размещена в стеке или должна «уходить» в кучу. Переменные, на которые есть ссылки за пределами функции, размещаются в куче.
Вопрос
Как увидеть результаты escape analysis?
Ответ
С помощью флага:
go build -gcflags="-m" main.go
Вопрос
Что такое GOGC?
Ответ
GOGC — это переменная окружения, управляющая частотой сборки мусора. По умолчанию GOGC=100, что означает: запускать GC, когда объём живых данных удвоится. Уменьшение значения ускоряет GC, но увеличивает нагрузку; увеличение — наоборот.
Вопрос
Как кросс-компилировать Go-программу?
Ответ
Установкой переменных окружения GOOS и GOARCH:
GOOS=linux GOARCH=amd64 go build -o app-linux main.go
GOOS=windows GOARCH=amd64 go build -o app.exe main.go
Вопрос
Поддерживает ли Go горячую перезагрузку кода?
Ответ
Нет, встроенная поддержка отсутствует. Для разработки используются сторонние инструменты, такие как air или gow.
Вопрос
Как обрабатывать сигналы ОС в Go?
Ответ
С помощью пакета os/signal:
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
<-sigChan // ожидание сигнала
Вопрос
Что такое инициализация пакета (init)?
Ответ
Функция init вызывается автоматически при запуске программы, до main. Она используется для настройки состояния пакета. В одном пакете может быть несколько init, выполняемых в порядке объявления.
Вопрос
В каком порядке выполняются init?
Ответ
Сначала init зависимых пакетов (в порядке импорта), затем init текущего пакета, и только потом main.
Стандартная библиотека и продвинутые темы
Вопрос
Как прочитать весь файл в строку?
Ответ
С помощью функции os.ReadFile:
data, err := os.ReadFile("file.txt")
if err != nil {
// обработка ошибки
}
text := string(data)
Вопрос
Как записать строку в файл?
Ответ
С помощью функции os.WriteFile:
err := os.WriteFile("output.txt", []byte("hello"), 0644)
Вопрос
Как открыть файл для построчного чтения?
Ответ
С помощью bufio.Scanner:
file, _ := os.Open("file.txt")
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
// обработка
}
Вопрос
Как создать HTTP-сервер на Go?
Ответ
С помощью пакета net/http:
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello")
})
http.ListenAndServe(":8080", nil)
Вопрос
Как отправить HTTP-запрос?
Ответ
С помощью клиента из net/http:
resp, err := http.Get("https://example.com")
if err != nil {
// обработка
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
Вопрос
Как сериализовать структуру в JSON?
Ответ
С помощью json.Marshal:
data, err := json.Marshal(myStruct)
Вопрос
Как десериализовать JSON в структуру?
Ответ
С помощью json.Unmarshal:
var v MyStruct
err := json.Unmarshal(jsonData, &v)
Вопрос
Как игнорировать поле при сериализации в JSON?
Ответ
Указать тег json:"-":
type User struct {
Name string
Password string `json:"-"`
}
Вопрос
Как задать альтернативное имя поля в JSON?
Ответ
Через тег json:
type Person struct {
FullName string `json:"name"`
}
Вопрос
Поддерживает ли encoding/json встраиваемые структуры?
Ответ
Да. Поля встраиваемых структур сериализуются как часть внешней структуры, если не указано иное.
Вопрос
Как парсить дату и время?
Ответ
С помощью time.Parse:
t, err := time.Parse("2006-01-02", "2025-12-31")
Вопрос
Почему в Go используется макет "2006-01-02"?
Ответ
Потому что 02 января 2006 года (01/02 03:04:05PM '06 -0700) — это мнемонический шаблон, где каждая часть времени соответствует своему значению: год=2006, месяц=01, день=02 и так далее.
Вопрос
Как получить текущее время?
Ответ
С помощью time.Now().
Вопрос
Как работать с часовыми поясами?
Ответ
Метод In позволяет преобразовать время в нужный часовой пояс:
loc, _ := time.LoadLocation("Europe/Moscow")
moscowTime := time.Now().In(loc)
Вопрос
Как использовать шаблоны в Go?
Ответ
С помощью пакета text/template (для текста) или html/template (для HTML с автоматическим экранированием):
tmpl := template.Must(template.New("test").Parse("Hello {{.Name}}"))
tmpl.Execute(os.Stdout, map[string]string{"Name": "Alice"})
Вопрос
Что такое template.Must?
Ответ
template.Must — это вспомогательная функция, которая вызывает panic, если переданный шаблон содержит ошибку. Используется при инициализации, когда ошибка означает фатальный сбой.
Вопрос
Как реализовать пользовательский тип ошибки?
Ответ
Определить структуру и реализовать метод Error() string:
type MyError struct {
Message string
}
func (e MyError) Error() string {
return e.Message
}
Вопрос
Что такое io.Reader и io.Writer?
Ответ
io.Reader — интерфейс для чтения данных:
Read(p []byte) (n int, err error)
io.Writer — интерфейс для записи:
Write(p []byte) (n int, err error)
Они лежат в основе всей системы ввода-вывода в Go.
Вопрос
Как соединить Reader и Writer?
Ответ
С помощью io.Copy:
io.Copy(dstWriter, srcReader)
Вопрос
Что такое bytes.Buffer?
Ответ
bytes.Buffer — это структура, реализующая и Reader, и Writer, и позволяющая эффективно накапливать и считывать байты в памяти.
Вопрос
Как работает сборщик мусора в Go?
Ответ
Go использует конкурентный, трёхцветный, инкрементальный сборщик мусора на основе алгоритма mark-and-sweep. Он работает параллельно с программой и стремится минимизировать паузы (обычно < 100 мкс).
Вопрос
Можно ли отключить сборщик мусора?
Ответ
Да, установив GOGC=off, но это крайне не рекомендуется. Программа будет потреблять всё больше памяти без освобождения.
Вопрос
Что такое планировщик Go (Go scheduler)?
Ответ
Планировщик Go управляет выполнением горутин на ограниченном числе потоков ОС (M:N scheduling). Он использует модель GMP: Goroutines (G), Threads (M), Processors (P). Это обеспечивает эффективную конкурентность даже при большом числе горутин.
Вопрос
Что такое runtime.GOMAXPROCS?
Ответ
GOMAXPROCS определяет максимальное число потоков ОС, которые могут одновременно выполнять код Go. По умолчанию равно числу логических CPU.
Вопрос
Как принудительно запустить сборку мусора?
Ответ
С помощью runtime.GC(). Однако это редко нужно в production, так как GC работает автоматически.
Вопрос
Что такое finalizer в Go?
Ответ
Финализатор — это функция, привязанная к объекту через runtime.SetFinalizer, которая вызывается перед освобождением памяти. Использование не рекомендуется, так как поведение не гарантируется.
Вопрос
Как проверить, является ли значение нулевым для любого типа?
Ответ
С помощью рефлексии:
reflect.ValueOf(v).IsZero()
(Доступно с Go 1.13.)
Вопрос
Что такое generics в Go?
Ответ
Generics — это механизм параметризации типов, добавленный в Go 1.18. Он позволяет писать функции и структуры, работающие с разными типами без дублирования кода.
Вопрос
Как объявить обобщённую функцию?
Ответ
Пример функции Min:
func Min[T constraints.Ordered](a, b T) T {
if a < b {
return a
}
return b
}
Вопрос
Что такое constraint в generics?
Ответ
Constraint — это интерфейс, ограничивающий набор допустимых типов для параметра типа. Например, constraints.Ordered разрешает только сравнимые типы (int, string и т.д.).
Вопрос
Можно ли использовать generics с каналами?
Ответ
Да. Пример:
func send[T any](ch chan<- T, val T) {
ch <- val
}
Вопрос
Как реализовать пул объектов?
Ответ
С помощью sync.Pool:
var pool = sync.Pool{
New: func() interface{} {
return &MyStruct{}
},
}
obj := pool.Get().(*MyStruct)
// использование
pool.Put(obj)
Вопрос
Когда использовать sync.Pool?
Ответ
Только для часто создаваемых и дорогих временных объектов, таких как буферы. Не подходит для хранения состояния или редко используемых объектов.
Вопрос
Что такое unsafe-пакет?
Ответ
Пакет unsafe предоставляет низкоуровневые операции, обходящие безопасность типов: указатели, размеры, выравнивание. Его использование нарушает гарантии языка и должно быть крайней мерой.
Вопрос
Можно ли преобразовать []byte в string без аллокаций?
Ответ
Стандартным способом — нет, так как string неизменяем, а []byte — изменяем. Но с помощью unsafe можно обойти это, хотя это небезопасно и не рекомендуется.
Вопрос
Как убедиться, что интерфейс не равен nil, даже если его значение nil?
Ответ
Интерфейс состоит из типа и значения. Если переменная объявлена как интерфейс и ей присвоен nil конкретного типа, интерфейс не будет равен nil. Проверка должна учитывать обе части.
Вопрос
Что такое «nil-интерфейс» на самом деле?
Ответ
Nil-интерфейс — это интерфейс, у которого и тип, и значение равны nil. Только такой интерфейс даёт true при сравнении с nil.
Вопрос
Как избежать утечек горутин?
Ответ
Всегда связывать жизненный цикл горутины с контекстом или каналом завершения. Использовать context.WithCancel и следить за ctx.Done().
Вопрос
Что такое deadlock в Go?
Ответ
Deadlock — это ситуация, когда все горутины заблокированы и не могут продолжить выполнение. Программа завершается с паникой: fatal error: all goroutines are asleep - deadlock!
Вопрос
Как протестировать конкурентный код?
Ответ
Использовать -race, писать тесты с разными порядками выполнения, применять testing.T.Parallel(), а также эмулировать задержки и отказы.
Вопрос
Что делает go doc?
Ответ
Генерирует документацию из комментариев к пакетам, функциям и типам в формате, понятном человеку. Комментарий должен начинаться с имени сущности.
Вопрос
Как написать хороший комментарий к функции?
Ответ
Комментарий должен начинаться с имени функции и описывать её назначение, параметры и поведение. Пример:
// Add returns the sum of a and b.
func Add(a, b int) int { ... }
Вопрос
Что такое build tags?
Ответ
Build tags — это комментарии в начале файла, управляющие компиляцией в зависимости от платформы или флагов. Пример:
//go:build linux
Вопрос
Как исключить файл из сборки?
Ответ
С помощью тега:
//go:build ignore
Вопрос
Что такое go:generate?
Ответ
Директива //go:generate указывает команду, которая будет выполнена при запуске go generate. Используется для генерации кода (например, mock-объектов, шаблонов).
Вопрос
Как обрабатывать большие файлы без загрузки в память?
Ответ
Использовать потоковую обработку: читать блоками через bufio.Reader, обрабатывать и сразу записывать или отбрасывать.
Вопрос
Что такое io.ReadCloser?
Ответ
Это интерфейс, объединяющий io.Reader и io.Closer. Часто возвращается функциями открытия файлов или HTTP-клиентом. Требует вызова Close() после использования.
Вопрос
Как корректно закрыть io.ReadCloser?
Ответ
С помощью defer:
resp, _ := http.Get(url)
defer resp.Body.Close()
Вопрос
Что происходит, если не закрыть resp.Body?
Ответ
Соединение остаётся открытым, что может привести к утечке ресурсов и исчерпанию пула соединений.
Вопрос
Как реализовать middleware для HTTP-обработчика?
Ответ
Middleware — это функция, принимающая http.Handler и возвращающая http.Handler:
func logging(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println(r.URL.Path)
next.ServeHTTP(w, r)
})
}
Вопрос
Как передать данные между middleware?
Ответ
Через контекст запроса:
ctx := context.WithValue(r.Context(), key, value)
next.ServeHTTP(w, r.WithContext(ctx))
Вопрос
Что такое http.HandlerFunc?
Ответ
Это адаптер, позволяющий использовать обычную функцию как http.Handler. Любая функция с сигнатурой func(http.ResponseWriter, *http.Request) автоматически преобразуется в http.HandlerFunc.
Вопрос
Как обработать CORS в Go?
Ответ
Добавить заголовки вручную или использовать middleware:
w.Header().Set("Access-Control-Allow-Origin", "*")
Вопрос
Как реализовать graceful shutdown HTTP-сервера?
Ответ
С использованием context и Shutdown:
srv := &http.Server{Addr: ":8080", Handler: mux}
go srv.ListenAndServe()
<-sigChan
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
srv.Shutdown(ctx)
Вопрос
Что такое filepath.Walk?
Ответ
Функция для рекурсивного обхода файловой системы. Вызывает переданную функцию для каждого файла и директории.
Вопрос
Как создать временную директорию?
Ответ
С помощью os.MkdirTemp:
dir, err := os.MkdirTemp("", "myapp-*")
Вопрос
Как удалить директорию рекурсивно?
Ответ
С помощью os.RemoveAll.
Вопрос
Что такое embed в Go?
Ответ
Пакет embed (Go 1.16+) позволяет встраивать файлы и директории прямо в бинарник программы с помощью директивы //go:embed.
Вопрос
Как встроить файл в бинарник?
Ответ
import _ "embed"
//go:embed version.txt
var version string
Вопрос
Что такое testing.TB?
Ответ
Интерфейс, общий для *testing.T и *testing.B, позволяющий писать вспомогательные функции, совместимые с тестами и бенчмарками.
Вопрос
Как пропустить тест при определённых условиях?
Ответ
С помощью t.Skip():
if runtime.GOOS == "windows" {
t.Skip("not supported on Windows")
}
Вопрос
Что такое table-driven test?
Ответ
Это паттерн тестирования, при котором множество случаев описываются в виде таблицы (среза структур), и один цикл прогоняет все кейсы. Это упрощает поддержку и расширение тестов.
Вопрос
Как мокать зависимости в Go?
Ответ
Через интерфейсы и внедрение зависимостей. Можно также использовать генераторы моков, такие как gomock или mockery.
Вопрос
Что такое dependency injection в Go?
Ответ
Передача зависимостей (сервисов, клиентов) в конструктор структуры или функцию, а не создание их внутри. Это улучшает тестируемость и гибкость.
Вопрос
Как избежать циклических импортов?
Ответ
Вынести общий интерфейс в отдельный пакет, использовать функции вместо глобальных переменных, пересмотреть архитектуру.
Вопрос
Что такое init cycle?
Ответ
Цикл инициализации возникает, когда два или более пакета зависят друг от друга через глобальные переменные или init. Компилятор выдаст ошибку.
Вопрос
Как работает recover внутри вложенной функции?
Ответ
recover работает только в функции, вызванной отложенно (defer). Если panic произошёл вне этой функции, recover не сработает.
Вопрос
Можно ли использовать panic в библиотеке?
Ответ
Нет. Библиотеки должны возвращать ошибки, а не вызывать panic. Исключение — фатальные ошибки инициализации, которые делают дальнейшую работу невозможной.
Вопрос
Что такое runtime.NumGoroutine()?
Ответ
Функция, возвращающая текущее количество активных горутин. Полезна для отладки утечек.
Вопрос
Как измерить время выполнения функции?
Ответ
С помощью time.Since:
start := time.Now()
doSomething()
elapsed := time.Since(start)
Вопрос
Что такое strings.Builder?
Ответ
Эффективная структура для конкатенации строк без лишних аллокаций. Реализует io.Writer.
Вопрос
Почему + для строк неэффективен в цикле?
Ответ
Потому что строки неизменяемы, и каждая операция + создаёт новую строку, копируя предыдущее содержимое. Это приводит к квадратичной сложности.
Вопрос
Как клонировать срез?
Ответ
С помощью copy:
newSlice := make([]T, len(oldSlice))
copy(newSlice, oldSlice)
Вопрос
Как сравнить два среза?
Ответ
Стандартным способом — только поэлементно. Для примитивов можно использовать reflect.DeepEqual, но он медленный. Начиная с Go 1.21, можно использовать slices.Equal.
Вопрос
Что такое slices пакет?
Ответ
Пакет slices (Go 1.21+) предоставляет общие функции для работы с срезами: Contains, Equal, Sort, Delete и другие.
Вопрос
Как отсортировать срез?
Ответ
С помощью sort.Slice:
sort.Slice(users, func(i, j int) bool {
return users[i].Name < users[j].Name
})
Вопрос
Что такое maps пакет?
Ответ
Пакет maps (Go 1.21+) содержит утилиты для работы с картами: Clone, Copy, Equal.
Вопрос
Как клонировать карту?
Ответ
Вручную:
dst := make(map[K]V, len(src))
for k, v := range src {
dst[k] = v
}
Или с Go 1.21: maps.Clone(src).
Вопрос
Что такое errors.Join?
Ответ
Функция (Go 1.20+), объединяющая несколько ошибок в одну. Полезна при агрегировании ошибок из нескольких источников.
Вопрос
Как обрабатывать несколько ошибок одновременно?
Ответ
Собрать их в срез и использовать errors.Join, либо вернуть первую, либо реализовать кастомную логику.
Вопрос
Что такое any в Go?
Ответ
any — это псевдоним для interface{}, введённый в Go 1.18 для улучшения читаемости.
Вопрос
Что такое comparable-ограничение в generics?
Ответ
Это встроенный constraint comparable, разрешающий только типы, которые можно сравнивать с помощью == и !=. Используется, например, для ключей карт в обобщённых структурах.
Вопрос
Можно ли использовать generics для создания обобщённой карты?
Ответ
Да:
type Map[K comparable, V any] map[K]V
Вопрос
Что такое type parameter?
Ответ
Type parameter — это параметр типа в обобщённой функции или структуре, обозначаемый в квадратных скобках: [T any].
Вопрос
Как ограничить тип только структурами?
Ответ
Напрямую нельзя. Go не предоставляет constraint для «только структуры». Можно использовать workaround через интерфейсы, но это не гарантирует тип.
Вопрос
Что такое instantiation в контексте generics?
Ответ
Instantiation — это процесс подстановки конкретных типов вместо параметров типа при вызове обобщённой функции или создании обобщённого типа.
Вопрос
Как отладить программу на Go?
Ответ
С помощью dlv (Delve) — официального отладчика Go. Поддерживает точки останова, инспекцию переменных, горутин и стека.
Вопрос
Что такое go install?
Ответ
Команда go install компилирует и устанавливает исполняемый файл в $GOBIN. Используется для установки CLI-инструментов.
Вопрос
Как обновить Go до новой версии?
Ответ
Скачать новую версию с golang.org или использовать менеджер версий, например gvm.
Вопрос
Что такое GOROOT и GOPATH?
Ответ
GOROOT — путь к установленному Go. GOPATH — рабочая директория (устарела с модулями). Сейчас используется только для хранения bin и кэша.
Вопрос
Нужно ли устанавливать GOPATH вручную?
Ответ
Нет. С Go 1.11+ и модулями GOPATH используется по умолчанию ($HOME/go), но явная настройка не требуется.
Вопрос
Что такое минимальная поддерживаемая версия Go?
Ответ
Проекты обычно указывают её в go.mod. Рекомендуется поддерживать последние две стабильные версии.
Вопрос
Как обеспечить обратную совместимость?
Ответ
Не ломать публичный API, следовать SemVer, использовать //go:build для условной компиляции новых фич.
Вопрос
Что такое deprecation в Go?
Ответ
Go не имеет встроенного механизма депрекации, но можно использовать комментарии:
// Deprecated: use NewFunction instead.
func OldFunction() { ... }
Вопрос
Как писать идиоматичный Go-код?
Ответ
Следовать официальному руководству Effective Go, использовать go fmt, избегать избыточных абстракций, предпочитать композицию наследованию, обрабатывать ошибки явно.
Вопрос
Что такое «Go proverb»?
Ответ
Краткие принципы сообщества, например:
- «Don’t communicate by sharing memory, share memory by communicating.»
- «Clear is better than clever.»
- «Errors are values.»