Open10
ちょっとしたgoでのgenericsの利用例
![podhmo](https://res.cloudinary.com/zenn/image/fetch/s--GWKFblck--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/27cc184596.jpeg)
ちょっとしたgoでのgenericsの例や思いつきをあげていく
genericsの使い方について
ここだけは外から摂取した情報で、以降は思いついたものを適宜列挙していく感じにする
(まじめな例は簡単に見つかるのでまじめな例以外を記録しておくことにする)
![podhmo](https://res.cloudinary.com/zenn/image/fetch/s--GWKFblck--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/27cc184596.jpeg)
テスト上でError monadというかrustの!
みたいなことをするパッケージ。
genericsのsignatureの仕様でmethodがtype parametersを持てないところで無理やり考えたのが部分適用した関数を返す関数。多値の仕様との兼ね合いも考える必要があった。
![podhmo](https://res.cloudinary.com/zenn/image/fetch/s--GWKFblck--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/27cc184596.jpeg)
関数のsignatureの特殊化によって、巨大なinterfaceの実装を分割して使う。
シンプルな例
応用例に近づけた例
![podhmo](https://res.cloudinary.com/zenn/image/fetch/s--GWKFblck--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/27cc184596.jpeg)
paginationをやるときは使うことになりそうだよねというやつ。
![podhmo](https://res.cloudinary.com/zenn/image/fetch/s--GWKFblck--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/27cc184596.jpeg)
こういうのもあるか。前者は例えば json.Unmarshal()
のwrapperとか。それを後者を使うと、副作用ではなく戻り値で受け取る形式にできる。
- シンプルには型の強制(ポインタの強制)
- ゼロ値での初期化
func DecodeJSON[T any](r io.Reader) (T, error) {
var ob T
if err := json.NewDecoder(r).Decode(&ob); err != nil {
return err
}
return ob
}
型の強制はこういうやつ
func BindJSON[T any](b []byte, ob *T) error {
return json.Unmarshal(b, ob)
}
![podhmo](https://res.cloudinary.com/zenn/image/fetch/s--GWKFblck--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/27cc184596.jpeg)
一般的な実装を書いて、型を厳しくしたような実装も作れるかも。enum的な何かを引数に取る関数に使えそう。
func DefineEnum[T any](r *Router, defaultValue T, values ...T) *reflectopenapi.RegisterTypeAction {
dst := make([]interface{}, len(values)+1)
typedValue := T(defaultValue)
dst[0] = typedValue
for i, v := range values {
dst[i+1] = T(v)
}
return r.m.RegisterType(typedValue, func(ref *openapi3.Schema) {
ref.Default = dst[0]
ref.Enum = dst
})
}
func DefineStringEnum[T ~string](r *Router, defaultValue T, values ...T) *reflectopenapi.RegisterTypeAction {
return DefineEnum(r, defaultValue, values...)
}
func DefineIntEnum[T ~int](r *Router, defaultValue T, values ...T) *reflectopenapi.RegisterTypeAction {
return DefineEnum(r, defaultValue, values...)
}
![podhmo](https://res.cloudinary.com/zenn/image/fetch/s--GWKFblck--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/27cc184596.jpeg)
メソッドは型パラメーターを持てない制限が結構辛いのだけれど、struct typeに型変数を持たせて、メソッドを定義し、それをpackage globalなvarで持っておくみたいなことをすると、いくつかの関数群を一度に定義みたいなこともできそう。
例えばこれは、contextに値を封入したり取り出したりするgetter,setterのペアを作るようなコード(keyのユニーク性はもうちょっと頑張る必要がありそう)。
![podhmo](https://res.cloudinary.com/zenn/image/fetch/s--GWKFblck--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/27cc184596.jpeg)
ちょっとしたsliceのhelper
func prepend[T any](xs []T, x T) []T {
return append([]T{x}, xs...)
}