GoのcontextのValueのkeyの型を再考する

# はじめに
GoのContextパッケージでは`ctx = WithValue(ctx, key ,value)`でcontextに紐付けた値を`ctx.Value(key)`で取得します。

このときkeyとしては他のコードとの衝突をさけるため、以下のコメントにあるように`type`でオリジナルの型を定義するのが推薦されてます。


> // The provided key must be comparable and should not be of type
// string or any other built-in type to avoid collisions between
// packages using context. Users of WithValue should define their own
// types for keys. To avoid allocating when assigning to an
// interface{}, context keys often have concrete type
// struct{}. Alternatively, exported context key variables' static
// type should be a pointer or interface.
func WithValue(parent Context, key, val interface{}) Context {

その際、`struct{}`で定義することが割と多いようで、例えば、[DatadogのSpanKey](https://github.com/DataDog/dd-trace-go/blob/0543fdceec376b43b23031afc121db1bfc037458/ddtrace/tracer/context.go#L15)は以下のように定義されています。

```golang

type contextKey struct{}

var activeSpanKey = contextKey{}

// ContextWithSpan returns a copy of the given context which includes the span s.
func ContextWithSpan(ctx context.Context, s Span) context.Context {
	return context.WithValue(ctx, activeSpanKey, s)
}

```

では、仮にだれかが誤って`type`で型宣言せずにKeyを`struct{}`にて定義し、そのライブラリを利用したとき、他のライブラリ（例えばdatadogのspanなど）や自分が書いたコードのkeyとそのkeyは衝突しないのでしょうか？
と疑問に思ったのでその調査と原因を共有します。

# 確認
```golang
package main

import (
	"context"
	"fmt"
)

type contextKey struct{}
var key = contextKey{}

// 正しく書かれたライブラリを想定
type contextKey2 struct{}
var key2 = contextKey2{}

// 誤って書かれたライブラリを想定
var key3 = struct{}{}
// 誤って書かれたライブラリを想定
var key4 = struct{}{}

func main() {
	ctx := context.WithValue(context.Background(), key, "Go")
	fmt.Println(ctx.Value(key))  // Go
	fmt.Println(ctx.Value(key2)) // nil
	fmt.Println(ctx.Value(key3)) // nil
	fmt.Println(ctx.Value(key4)) // nil
	ctx = context.WithValue(ctx, key2, "Scala")
	fmt.Println(ctx.Value(key))  // Go
	fmt.Println(ctx.Value(key2)) // Scala
	fmt.Println(ctx.Value(key3)) // nil
	fmt.Println(ctx.Value(key4)) // nil
	ctx = context.WithValue(ctx, key, "Java")
	fmt.Println(ctx.Value(key))  // Java
	fmt.Println(ctx.Value(key2)) // Scala
	fmt.Println(ctx.Value(key3)) // nil
	fmt.Println(ctx.Value(key4)) // nil
	ctx = context.WithValue(ctx, key3, "Python")
	fmt.Println(ctx.Value(key))  // Java
	fmt.Println(ctx.Value(key2)) // Scala
	fmt.Println(ctx.Value(key3)) // Python
	fmt.Println(ctx.Value(key4)) // Python
        

	// 後ほど説明
	// fmt.Println(key == key2)  keyとkey2は比較不可能
	fmt.Println(key == key3) // true
	ikey := interface{}(key)
	ikey2 := interface{}(key2)
	ikey3 := interface{}(key3)

	fmt.Println(ikey == ikey2) // 比較可能になるが、false
	fmt.Println(ikey == ikey3) // false
}

```

結論からいいます。上記コードをみればわかるように衝突しません。
`key`や`key2`はそれぞれ他のキーに影響を受けずに変更、取得できています。
一方、誤ってかかれた`key3`への変更は`key3``key4`両方に影響しています。

# 原因

以下のcontextのソースの抜粋です。`Value`の実装を見ればわかりますが、引数のkeyと自分が保持しているkeyをinterface型として比較し、同一でない場合は自分が保持している親の`Context`に委譲していることがわかります。

```golang
func WithValue(parent Context, key, val interface{}) Context {
	if key == nil {
		panic("nil key")
	}
	if !reflect.TypeOf(key).Comparable() {
		panic("key is not comparable")
	}
	return &valueCtx{parent, key, val}
}

// A valueCtx carries a key-value pair. It implements Value for that key and
// delegates all other calls to the embedded Context.
type valueCtx struct {
	Context
	key, val interface{}
}

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

```

このことから、
`key`と`key3`をinteface{}型として比較したときにイコールにならなければ衝突しないといえそうです。
その確認コードは上記のコードにすでにあり、イコールにならないのです。

```golang

fmt.Println(ikey == ikey3) // false
```

# なぜ、イコールにならないか
もう一歩踏み込んでなぜ`ikey ==  ikey3`がfalseになるのかGolangのspecを確認しながらみていきましょう。

まず、`==`で比較可能なためには、代入可能性(Assignability)がないといけません。
このあたりのことは前回の[Goの代入可能性についてちゃんと理解してみる](https://qiita.com/behiron/items/89bf7292aec48b097fe4)
に詳しく書いてます。

一部代入可能性の条件を抜粋すると、
> A value x is assignable to a variable of type T ("x is assignable to T") if one of the following conditions applies:
1. x's type is identical to T.
2. x's type V and T have identical underlying types and at least one of V or T is not a defined type.

`key`と`key2`は比較不可能である理由は、`key`と`key2`ともに`defined type`であるからであるとわかります。

でも、`key` と `key3`は比較はできてしまいます。なぜなら`contextKey`型と`struct{}`型は`underlying type`である`struct{}`が同じで、`contextKey`型は`defined type`でないからです。
そして、構造体の同一性は
> Struct values are comparable if all their fields are comparable. Two struct values are equal if their corresponding non-blank fields are equal.

なので、`key`と`key3`は比較可能であるだけでなく、イコールになることがわかります。

ただ、実は上記の話は本筋から脱線してます。なぜなら`Value`のコードでは`interface{}`型同士を比較しているのでした。

`interface`の型同一性は以下でした。
> Two interface types are identical if they have the same set of methods with the same names and identical function types. Non-exported method names from different packages are always different. The order of the methods is irrelevant.

当然、`interface{}`型なので、`ikey`,`ikey2`,`ikey3`ともに型同一で、型同一ならば比較可能です。(ここはちょっと蛇足感はありますが、interface型の型同一性の確認のため引用しました)

では、==でイコールになるのはどのような条件でしょうか。仕様に

>Interface values are comparable. Two interface values are equal if they have identical dynamic types and equal dynamic values or if both have value nil.

とあります。`dynamic types`が同じである必要があるとあります。
`type`で型を宣言したものは

> The new type is called a defined type. It is different from any other type, including the type it is created from.

とあるように他の型と型同一になることがありません。
`contextKey`、`contextKey2`、`struct{}`ともに異なる型となります。
そのため、`ikey`と`ikey2`、`ikey`と`ikey3`ともにイコールにならないことがわかります。

以上です。

# 雑感
個人的には、`key`と`key3`を比較するとイコールなのに、それらを`interface{} `でキャストして比較した途端にイコールにならないというのは自明ではないので新たな発見でした。