Как пройти собеседование по Go: топ вопросов от А до Я

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Как пройти собеседование по Go: топ вопросов от А до Я

Собеседование на позицию Go-разработчика — это не просто проверка синтаксиса. Работодатели ищут кандидатов, которые понимают философию языка: простоту, конкурентность и эффективное управление памятью. В этой статье мы разберём ключевые вопросы, которые вам обязательно зададут — от основ до продвинутых тем с примерами кода. Подготовьтесь по нашему чек-листу, и вы будете чувствовать себя уверенно.

1. Основы Go: типы данных, структуры и управление памятью

Начнём с базы. Без неё никуда. Интервьюеры любят задавать вопросы о типах и их поведении.

Какие встроенные типы данных существуют в Go?

  • Базовые: int, float64, string, bool.
  • Агрегированные: array, struct.
  • Ссылочные: slice, map, channel, pointer, function.

Чем отличается массив от слайса?

Массив имеет фиксированную длину, заданную на этапе компиляции. Слайс — это динамическая структура, которая ссылается на базовый массив.

// Массив
var arr [3]int = [3]int{1, 2, 3}
arr[0] = 10 // Ок

// Слайс
slc := []int{1, 2, 3}
slc = append(slc, 4) // Длина может расти

Вопрос на засыпку: Что произойдёт с памятью при append, если ёмкость слайса превышена? (Ответ: Go выделит новый массив в 2 раза больше и скопирует данные).

Как устроена карта (map) в Go?

Map — это хеш-таблица. Порядок обхода не гарантирован. При чтении из несуществующего ключа возвращается нулевое значение (например, 0 для int).

m := make(map[string]int)
m["key"] = 42
value, ok := m["key"] // ok == true, если ключ существует

2. Конкурентность: горутины, каналы и мьютексы

Это самая важная тема для Go. Без неё собеседование не пройти.

Что такое горутина и чем она отличается от потока ОС?

Горутина — это легковесный поток, управляемый рантаймом Go. Стек горутины начинается с 2-4 КБ (против 1 МБ у потока ОС). Тысячи горутин работают на нескольких потоках ОС (модель M:N).

Как работают каналы? Приведите пример патерна worker pool.

func worker(id int, jobs <-chan int, results chan<- int) {
    for j := range jobs {
        results <- j * 2
    }
}

func main() {
    jobs := make(chan int, 100)
    results := make(chan int, 100)

    // Запускаем 3 воркера
    for w := 1; w <= 3; w++ {
        go worker(w, jobs, results)
    }

    // Отправляем задания
    for j := 1; j <= 5; j++ {
        jobs <- j
    }
    close(jobs)

    // Собираем результаты
    for r := 1; r <= 5; r++ {
        <-results
    }
}

В чём разница между буферизированным и небуферизированным каналом?

  • Небуферизированный: отправитель блокируется, пока получатель не примет сообщение. Синхронная связь.
  • Буферизированный: отправитель блокируется только когда буфер заполнен. Асинхронная связь.

Как избежать дедлока?

Используйте select с таймаутом или default:

select {
case msg := <-ch:
    fmt.Println(msg)
case <-time.After(1 * time.Second):
    fmt.Println("timeout")
}

3. Интерфейсы, дженерики и обработка ошибок

Go — не чисто ООП язык, но интерфейсы тут играют ключевую роль.

Как работает утиная типизация в Go?

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

type Stringer interface {
    String() string
}

type User struct {
    Name string
}

func (u User) String() string {
    return "User: " + u.Name
}

// User автоматически реализует Stringer
var s Stringer = User{Name: "Alice"}
fmt.Println(s.String()) // User: Alice

Что такое дженерики в Go 1.18+?

Позволяют писать функции и структуры, работающие с любыми типами.

func Min[T ~int | ~float64](a, b T) T {
    if a < b {
        return a
    }
    return b
}

func main() {
    fmt.Println(Min(10, 20)) // 10
    fmt.Println(Min(3.14, 2.71)) // 2.71
}

Как правильно обрабатывать ошибки?

Ошибки — это значения. Используйте errors.Is и errors.As для проверки цепочек ошибок. Никогда не игнорируйте ошибки (кроме defer в некоторых случаях).

import "errors"

var ErrNotFound = errors.New("item not found")

func GetItem(id int) (string, error) {
    if id != 42 {
        return "", ErrNotFound
    }
    return "item", nil
}

func main() {
    _, err := GetItem(1)
    if errors.Is(err, ErrNotFound) {
        fmt.Println("Not found!")
    }
}

4. Продвинутые вопросы: память, сборка мусора и профилирование

Для senior-позиций часто спрашивают про внутреннее устройство.

Как работает сборка мусора (GC) в Go?

Go использует concurrent, tri-color, mark-and

Блоги

Рекомендаций по книгам