On context.Context keys uniqueness

Jean-François Bustarret
Random Go tips
Published in
1 min readOct 14, 2015

After reading the blog post introducing context.Context, I got used to using integers as context.Context keys.

Here is a code sample taken from the blog post :

// userIPkey is the context key for the user IP address. Its value of zero is
// arbitrary. If this package defined other context keys, they would have
// different integer values.
const userIPKey key = 0
func WithUserIP(ctx context.Context, userIP net.IP) context.Context {
return context.WithValue(ctx, userIPKey, userIP)
}
func UserIPFromContext(ctx context.Context) (net.IP, bool) {
userIP, ok := ctx.Value(userIPKey).(net.IP)
return userIP, ok
}

This works fine when all context helper functions are located is the same package. But how would you guarantee key uniqueness when using multiple packages in a clean way ?

Fortunately, context.Context uses interface{} as a key parameter, and accepts everything that is comparable (i.e. that can be compared with ==):

func WithValue(parent Context, key interface{}, val interface{}) Context
func (c Context) Value(key interface{}) interface{}
func (c *valueCtx) Value(key interface{}) interface{} {
if c.key == key {
return c.val
}
...
}

Struct are comparable, and two struct values are equal if their types match and their fields are equal. Therefore we could use :

type userIPKey struct{}func WithUserIP(ctx context.Context, userIP net.IP) context.Context {
return context.WithValue(ctx, userIPKey{}, userIP)
}
func UserIPFromContext(ctx context.Context) (net.IP, bool) {
userIP, ok := ctx.Value(userIPKey{}).(net.IP)
return userIP, ok
}

Each package defines its own key types, and no conflicts are possible. Problem solved !

--

--