Как пройти собеседование по 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