plugはなぜ文字列をキーとして扱うのか
plugでは、キー文字列は「どの関数が置き換えられているか」を調べて、置き換えられていればその関数を返すために使っている。用途からすれば、関数ポインタで比較すれば実現できそうに思うので、
plug.Func[F ~func(T)R, T..., R... any](s string, f F)よりも単純に
plug.Func[F ~func(T)R, T..., R... any](f F)の方が分かりやすいのだが、文字列を使っている理由はGoの仕様として関数同士の比較はできないため。
関数ポインタの値を比較していた頃もあった
Section titled “関数ポインタの値を比較していた頃もあった”ところで実際は、言語としては保証していないが reflect.Value.Pointer を使えば、動作が保証されていなくても意図したように比較できる。
stretchr/testifyのように、厳密な動作を求められる場合は言語仕様に定義されていない動作を使うと問題になる。だけどplugの場合はテスト目的ではあるし、比較がうまくいかない環境が存在しても単にテストが落ちる(false positive)だけで、逆(false negative)はない。
少なくとも現在の主要な環境では動作するし、仮に動かない環境があってもその時に考えればいいかと思っていたが、ジェネリックな関数で破滅した。この時に調べた内容はGoで関数が同一かどうか調べるにまとめてある。
比較に使える他の値はあるか
Section titled “比較に使える他の値はあるか”全部を自分でコントロールできるコードなら context.Context のようなコンテナに入れて渡せばいいが、 time.Now を置き換える場合はそもそも引数が無いので渡しようがないし、シグネチャを変えてしまうとモンキーパッチの意味がなくなる。
他に plug.Get から参照できる値といえばスタックやゴルーチンIDだが、どちらも望ましい値ではない。
なので、plug.Set するテストケース側と plug.Get する生成されたコード側で共通のルールを用いて、置き換えるべき関数を一意に特定させる必要があった。その表現は揃っていればなんでもよかったが、go docコマンドで馴染みのある表現が分かりやすいだろう。