Для чего используется `context.WithValue` и какие есть особенности его применения?

Ответ

context.WithValue используется для передачи данных, связанных с конкретным запросом (request-scoped data), вниз по стеку вызовов без необходимости явно передавать их в качестве аргументов каждой функции. Это особенно полезно для сквозной передачи метаданных, таких как ID запроса, токен аутентификации или информация для трассировки.

context.WithValue возвращает копию родительского контекста с привязанной к нему новой парой ключ-значение.

Пример использования:

package main

import (
    "context"
    "fmt"
)

// Рекомендуется использовать пользовательский тип для ключа,
// чтобы избежать коллизий с ключами из других пакетов.
type contextKey string

const userKey = contextKey("user")

func processRequest(ctx context.Context) {
    // Извлекаем значение из контекста
    if user, ok := ctx.Value(userKey).(string); ok {
        fmt.Println("Processing request for user:", user)
    } else {
        fmt.Println("Processing request for anonymous user")
    }
}

func main() {
    // Создаем базовый контекст
    ctx := context.Background()

    // Создаем новый контекст со значением
    userCtx := context.WithValue(ctx, userKey, "admin")

    processRequest(userCtx) // Output: Processing request for user: admin
    processRequest(ctx)     // Output: Processing request for anonymous user
}

Ключевые особенности и лучшие практики:

  1. Используйте неэкспортируемые пользовательские типы для ключей. Это предотвращает случайные коллизии имен ключей между разными пакетами. Использование простого типа string в качестве ключа является анти-паттерном.
  2. Не используйте контекст для передачи обязательных параметров. Если функция требует для работы какой-то параметр, его следует передавать явно через аргументы. Это делает API вашей функции понятным и самодокументируемым. context.WithValue предназначен для опциональных, сквозных данных.
  3. Значения должны быть потокобезопасными. Контекст может передаваться между несколькими горутинами, поэтому любое значение, помещенное в него, должно быть безопасно для одновременного доступа.
  4. Контекст неизменяем (immutable). context.WithValue не изменяет существующий контекст, а возвращает новый, который является его потомком.