❗ エラーについて
Goで context.WithValue を使ってキーにstring型を指定すると、
以下のような警告が出ます。
should not use built-in type string as key for value; define your own type to avoid collisions (SA1029)
これは staticcheck によるコード品質向上のためのチェックの一つで、
「SA1029」というルールに該当します。
🤔 エラーの解説
なぜ string をキーにすると警告されるのか?
context.WithValue は内部的に key-value を持つツリー構造を作りますが、
キーの衝突があってもコンパイルエラーにならないという問題があります。
以下のように、複数の異なるパッケージで "traceID" という文字列をキーにしていた場合、
意図しない上書きや参照が起こる可能性があります。
ctx := context.WithValue(context.Background(), "traceID", 123)
value := ctx.Value("traceID") // 意図したものとは限らない
こうした衝突は静かに発生し、バグを見つけにくくします。
✅ 解決方法
解決策:独自の型を使う
Goではキーに使う値は何でもOKですが、
衝突を防ぐために「独自の型+ゼロ値(struct{})」を使うのが一般的です。
type traceIDKey struct{}
func SetTraceID(ctx context.Context, traceID int) context.Context {
return context.WithValue(ctx, traceIDKey{}, traceID)
}
func GetTraceID(ctx context.Context) (int, bool) {
val := ctx.Value(traceIDKey{})
if id, ok := val.(int); ok {
return id, true
}
return 0, false
}
🧠 なぜ空の構造体を使用するのか?
キーに string や int を使うと、それ自体がヒープに割り当てられる可能性があります。
またパッケージ間でキーを共有してしまうと、
予期せぬアクセスによってGCやデバッグのコストも上がります。
struct{} 型のキーは
- ゼロサイズ
- ユニーク性が高い(型が異なると別物)
- ヒープに割り当てられにくい
といった理由から、パフォーマンスと安全性の両面で優れているとされています。
🧩 まとめ
- context.WithValue のキーに string を使うと staticcheck に怒られる(SA1029)
- 理由は キーの衝突によるバグの温床になるため
- 解決策は「独自の unexported 型(例:**type traceIDKey struct{})」を定義すること
- 空の構造体 struct{} は GC負荷も小さく、Goの公式にも推奨されている使い方
✅ おまけ(サンプルコード)
良ければハンズオンに使用していただければと!
package trace
import "context"
type traceIDKey struct{}
func SetTraceID(ctx context.Context, id int) context.Context {
return context.WithValue(ctx, traceIDKey{}, id)
}
func GetTraceID(ctx context.Context) (int, bool) {
val := ctx.Value(traceIDKey{})
id, ok := val.(int)
return id, ok
}