使い方
- インストールコマンドをコピーし、自分の環境へ導入する前にソースを確認します。
- 必要なツール、認証情報、対応環境を原文で確認します。
- 入力と成果物を指定し、出力を資料と照合します。
npx skills add https://github.com/victorzhuk/skills --skill z-go-context開始前の確認
- 導入にはモデル契約、API枠、ファイルへのアクセス許可は含まれません。
- スター数や導入数は活動の指標であり、独立した品質・安全性試験ではありません。
見出し、例、コード、表、リンク、参照画像を含む原文を表示しています。
context.Context
Quick reference
| Situation | Use |
|---|---|
Entry point (main, init, test body) | context.Background() |
| Function needs ctx but caller doesn't supply one yet | context.TODO() |
| HTTP handler | r.Context() |
| Manual cancellation | context.WithCancel(parent) |
| Relative timeout | context.WithTimeout(parent, d) |
| Absolute deadline | context.WithDeadline(parent, t) |
| Background work must outlive request (Go 1.21+) | context.WithoutCancel(ctx) |
\* context.AfterFunc(ctx, fn) (Go 1.21+) runs fn in its own goroutine when ctx is done — an alternative to WithoutCancel + an explicit select for pure cleanup. Zero use in practice so far; reach for it only if the select pattern below doesn't fit.
Core rules
ctxis always the first parameter, always namedctx context.Context.- Never store context in a struct — pass it through function parameters.
- Never pass
nil— usecontext.TODO()if you don't have one yet. - Never create
context.Background()mid-request — propagate the caller's context. - Always
defer cancel()immediately afterWithCancel/WithTimeout/WithDeadline. - Context value keys must be unexported types; bare strings collide across packages.
Propagation
Break the chain and cancellation stops propagating:
// bad — new root context, client disconnect ignored
func (s *OrderService) Create(ctx context.Context, o Order) error {
return s.db.ExecContext(context.Background(), query, o.ID)
}
// good
func (s *OrderService) Create(ctx context.Context, o Order) error {
return s.db.ExecContext(ctx, query, o.ID)
}Always use context-aware variants: QueryContext, ExecContext, NewRequestWithContext.
Cancellation
WithCancel lets a parent scope request shutdown of cooperating downstream work. Cancellation does not wait for workers or interrupt code that ignores it; join workers before releasing resources they use. Call cancel() as soon as the reason is gone — the defer handles the normal path; call it explicitly on error to signal early:
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if err := doWork(ctx); err != nil {
cancel() // signal any concurrent workers
return fmt.Errorf("run: %w", err)
}For fan-out patterns see [[z-go-concurrency]].
Timeouts and deadlines
Always defer cancel() — discarding the cancel return leaks the timer resource.
// relative — most common
func (s *UserService) Get(ctx context.Context, id string) (*User, error) {
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel()
return s.repo.FindByID(ctx, id)
}
// absolute — use for SLA-bound batch work
func (s *BatchService) Run(ctx context.Context, b Batch) error {
ctx, cancel := context.WithDeadline(ctx, b.SLADeadline)
defer cancel()
// ...
}Nested timeouts: the shorter deadline always wins. A 10s child of a 2s parent expires at 2s.
Listening for cancellation
func poll(ctx context.Context, interval time.Duration) error {
ticker := time.NewTicker(interval)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return ctx.Err()
case <-ticker.C:
if err := doWork(ctx); err != nil {
return fmt.Errorf("poll: %w", err)
}
}
}
}For CPU-bound loops, check ctx.Err() != nil periodically instead of select.
WithoutCancel (Go 1.21+)
Preserves context values but removes cancellation and deadline; Done() is nil. Use only when work deliberately outlives the request. Give it its own bound with context.WithTimeout(context.WithoutCancel(ctx), limit), and arrange application-owned shutdown and joining through [[z-go-concurrency]]. A timeout alone does not make ignored cancellation safe or guarantee delivery of audit records. For work that must survive process exit, use durable delivery.
context.Background() would drop request values. Keeping ctx would retain request cancellation. Neither choice replaces an explicit worker lifetime. See context.WithoutCancel and CancelFunc.
Context values
type contextKey string
const (
traceIDKey contextKey = "trace_id"
requestIDKey contextKey = "request_id"
)
func WithTraceID(ctx context.Context, id string) context.Context {
return context.WithValue(ctx, traceIDKey, id)
}
func TraceIDFromContext(ctx context.Context) (string, bool) {
v, ok := ctx.Value(traceIDKey).(string)
return v, ok
}Bare string keys (context.WithValue(ctx, "trace_id", ...)) collide across packages — never use them.
Context values are for cross-cutting, request-scoped metadata: trace/request IDs, authenticated user or tenant, and loggers enriched with trace fields. Everything else — DB connections, feature flags, business-logic parameters — belongs in constructors or function arguments.
HTTP handlers
Use r.Context() — never context.Background(). After a service call fails, check ctx.Err() != nil before writing an error response (client may already be gone). Middleware enriches context via r.WithContext(ctx) before the handler runs.
Verify
go vet ./...
staticcheck ./... # SA1012 (nil ctx), SA4006 (unused value); go vet checks lostcancel
