Go-контексты без боли — как понять context.Context и зачем он везде

Разбираем, почему context.Context встречается почти в каждой функции на Go, как он спасает от зависаний и управляет временем жизни операций.

Разработка

6 мин

Go-разработчики быстро привыкают к синтаксису: функции, интерфейсы, ошибки. Но почти сразу после "Hello, world" в коде начинает появляться что-то загадочное — context.Context. И не просто где-то в глубине пакета, а прямо в сигнатуре каждой второй функции. Зачем он нужен и почему без него никуда?

Давайте разберёмся.

Что такое context в Go

context.Context — это специальный объект, который передаётся по цепочке вызовов и управляет:

  • временем жизни операций,

  • отменой процессов,

  • дедлайнами,

  • передачей метаданных (например, ID запроса).

Контекст — это как "невидимый рюкзак", который функция получает вместе с вызовом и из которого можно достать полезную информацию.

Где чаще всего встречается context

  1. Работа с сетевыми запросами — отмена по таймауту, если ответ слишком долго не приходит.

  2. Запросы к БД — чтобы не держать соединение "висящим" бесконечно.

  3. Фоновые горутины — graceful shutdown при завершении сервиса.

Почему он почти в каждом аргументе

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

func fetchUser(ctx context.Context, id int) (User, error) {
    row := db.QueryRowContext(ctx, "SELECT name FROM users WHERE id=?", id)
    ...
}

Если запросу поставили ctx с таймаутом, то любая операция внутри автоматически остановится по истечении времени.

Важные приёмы работы

Создание контекста с таймаутом:

ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()

Отмена вручную:

ctx, cancel := context.WithCancel(context.Background())
go func() {
    time.Sleep(time.Second)
    cancel() // остановим работу всех, кто слушает этот ctx
}()

Передача значений (аккуратно!):

ctx = context.WithValue(ctx, "requestID", "abc123")

Важно: злоупотреблять WithValue не стоит — это скорее "метаданные", а не полноценные данные.

Типичные ошибки и ловушки

  • Не вызвали cancel() → утечка ресурсов.

  • Пихаем всё подряд в WithValue → теряется читаемость.

  • Не передаём context дальше → лишаем функцию возможности управлять собой.

Главная идея context проста: контроль должен быть у вызывающего кода, а не у вызываемого. Именно поэтому в Go почти каждая публичная функция в пакете стандартной библиотеки начинается с аргумента ctx.

Контекст — это не просто модное слово в Go. Это встроенный инструмент, который делает ваши программы предсказуемыми, управляемыми и защищёнными от зависаний. Чем раньше начнёте правильно его использовать — тем меньше "боли" в будущем.

У Кодика появился новый топовый курс по Go 🚀
Это мощный и понятный старт для тех, кто хочет освоить язык, на котором пишут быстрые и надёжные сервисы. В курсе простыми словами объясняем всё: от первых шагов до серьёзных концепций вроде горутин и контекстов.

А ещё у нас есть уютный telegram-канал, где мы делимся новостями из мира IT, разбираем интересные задачи и общаемся с комьюнити. Подписывайся, чтобы не пропускать самое важное.

Комментарии