メソッドのジェネリクスでGoの夢は広がるか? / Kyoto.go #65
by
utagawa kiki
×
Copy
Open
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Slide 1
Slide 1 text
メソッドのジェネリクスで Goの夢は広がるか? id:utgwkk / @utgwkk (うたがわきき) 2026/6/6 Kyoto.go #65 オフラインLT会@はてな 1
Slide 2
Slide 2 text
自己紹介 ● うたがわきき (@utgwkk) ● 株式会社はてな ○ Webアプリケーションエンジニア ○ 京都在住 ● 好きなパッケージはreflect 2
Slide 3
Slide 3 text
今日の流れ ● Go 1.27で導入されるメソッドのジェネリク スの話をします ● その前に、これまでのGoのジェネリクスの話 をおさらいします 3
Slide 4
Slide 4 text
おしながき ● ジェネリクス以前 ○ Go ~1.17 ● ジェネリクス以降 ○ Go 1.18~ ● メソッドのジェネリクスで広がる夢 ○ Go 1.27?~ 4
Slide 5
Slide 5 text
ジェネリクス以前 (~1.17) ● 任意の型に対する操作を書くときの選択肢は 以下3つ ○ any型からキャスト ○ リフレクション ○ コード生成 ● 長らくこれらで暮らしてきた 5
Slide 6
Slide 6 text
any型からキャスト ● 任意の型を入れるコンテナ ○ context.Context ○ sync.Map ● キャストして所望の型として扱う ○ val, ok := ctx.Value(...).(*MyType) 6
Slide 7
Slide 7 text
リフレクション (1) ● プログラムが実行時に自身の構造を操作する ○ reflectパッケージを利用 ● 任意の型を受け取って値を書き換える関数は だいたいリフレクションやってた ○ ジェネリクスの導入で数は減った 7
Slide 8
Slide 8 text
リフレクション (2) ● 自力でリフレクションするのは修羅 ○ 失敗すると問答無用でpanicする ○ やらなくて済むなら回避したほうがいい ● 遅い 8
Slide 9
Slide 9 text
リフレクションの活用事例 (1) ● 標準ライブラリ ○ database/sql ○ encoding/json, encoding/xml ○ fmt ○ sort ○ text/template, html/template 9
Slide 10
Slide 10 text
リフレクションの活用事例 (2) ● サードパーティーライブラリ ○ github.com/caarlos0/env ■ 環境変数→structのマッピング ○ github.com/go-playground/validator ■ バリデーション ○ github.com/stretchr/testify ■ テストのassertion 10
Slide 11
Slide 11 text
コード生成 (1) ● プログラム中で使う範囲の実装を あらかじめ生成しておく ● スキーマと相性が良い ○ OpenAPI ○ GraphQL ○ protobuf 11
Slide 12
Slide 12 text
コード生成 (2) ● コード生成の対象が多いとコード量が増える ○ Git管理するか議論になりがち ● パフォーマンスは出しやすい 12
Slide 13
Slide 13 text
コード生成の活用事例 ● github.com/abice/go-enum ○ enum型の定義を生成 ● go.uber.org/mock/mockgen ○ interfaceのモック実装を生成 ● github.com/99designs/gqlgen ○ GraphQL APIのリゾルバ実装などを生成 13
Slide 14
Slide 14 text
おさらい: ジェネリクス以前 (~1.17) ● 任意の型に対する操作を書くときの選択肢は 以下3つ ○ any型からキャスト ○ リフレクション ○ コード生成 ● 長らくこれらで暮らしてきた 14
Slide 15
Slide 15 text
ジェネリクスの導入 (1.18) ● 汎用的な関数・型をジェネリクスで書ける ● 嬉しさ ○ 型・関数を使い回せる (場合がある) ○ リフレクションが不要になる (場合がある) ○ シグネチャがより直感的になる (場合がある) ● ただし制約が強い 15
Slide 16
Slide 16 text
型・関数を使い回せる ● database/sql ○ nullableなカラムを表す型が分かれていた ■ bool, []byte, float64, int{16,32,64}, string, time.Time 向けの型がそれぞれ定義されていた ○ Null[T] 型を使えばよくなった 16
Slide 17
Slide 17 text
リフレクションが不要になる ● スライスのsort ○ sort.Slice関数は内部でリフレクションしていた ■ sort.Interfaceを実装した型を用意したらリフレクション不 要にできるが、煩雑 ○ slices.Sort関数はリフレクションしない ■ 比較関数を渡したらよい 17
Slide 18
Slide 18 text
シグネチャがより直感的になる (1) ● any型を渡すような関数の制約が明確になる ○ 実行時エラーに悩まされづらくなる ● errors.AsType関数 ○ errors.As関数の代替 ○ エラーをUnwrapして型キャストする ○ 間違った使い方をコンパイルエラーで検出できる 18
Slide 19
Slide 19 text
シグネチャがより直感的になる (2) var asErr *SomeError if ok := errors.As(err, &asErr); ok { // asErr != nil } // 第2引数はerrorじゃなくても渡せてしまう 19
Slide 20
Slide 20 text
シグネチャがより直感的になる (3) if asErr, ok := errors.AsType[*SomeError](err); ok { // asErr != nil } // ジェネリクスの型制約で errorであることが // コンパイル時に保証されている 20
Slide 21
Slide 21 text
Goのジェネリクスの限界 (1) ● メソッドに型引数を渡せない ○ func (x *List[T]) Map[U any](...) ■ 文法的には書けるがコンパイルエラー ○ Option型とか作るときに困りがち ■ メソッドにできないので関数にする 21
Slide 22
Slide 22 text
Goのジェネリクスの限界 (2) ● ジェネリックなinterfaceのメソッドへの対応 が困難だったため ○ 独自型のメソッドをジェネリクス対応するなら interfaceもジェネリクス対応しないといけなくなる ○ ジェネリックなinterfaceのメソッド呼び出しを効率 的に実装できない 22
Slide 23
Slide 23 text
おさらい: ジェネリクスの導入 (1.18) ● 汎用的な関数・型をジェネリクスで書ける ● 嬉しさ ○ 型・関数を使い回せる (場合がある) ○ リフレクションが不要になる (場合がある) ○ シグネチャがより直感的になる (場合がある) ● ただし制約が強い 23
Slide 24
Slide 24 text
spec: generic methods for Go #77273 https://github.com/golang/go/issues/77273 24
Slide 25
Slide 25 text
25 https://github.com/golang/go/issues/77273#issuecomment-3962618141
Slide 26
Slide 26 text
おさらい: Goのジェネリクスの限界 (2) ● ジェネリックなinterfaceのメソッドへの対応 が困難だったため ○ 独自型のメソッドをジェネリクス対応するなら interfaceもジェネリクス対応しないといけなくなる ○ ジェネリックなinterfaceのメソッド呼び出しを効率 的に実装できない 26
Slide 27
Slide 27 text
認識の変化 (1) ● ジェネリックなメソッドは、interfaceに対し て実装されないとしても、コードの名前空間 を整理するのに有用である ○ >Therefore methods are useful for organizing code even if they don't ever implement an interface. 27
Slide 28
Slide 28 text
認識の変化 (2) ● c(b(a(x))) よりも x.a().b().c() のほうが読み やすい ○ そうですね 28
Slide 29
Slide 29 text
メソッドのジェネリクス ● Go 1.27で実装予定 ● 具体型のメソッドに型引数を渡せる ○ interfaceのメソッドは引き続き未対応 29
Slide 30
Slide 30 text
夢: コレクション操作 ● xs.Filter(...).Map(...) のようにジェネリック なコレクション操作を記述できる ○ Filterは書けたけどMapが無理だった ■ 任意の型に変わりうるため ○ Filter(Map(xs, ...), ...) より読みやすいのでは 30
Slide 31
Slide 31 text
夢: Option・Result型 ● structで実装すればメソッドチェーンできる ○ Some(x).Map(...) ○ None() とは書けなくて型引数が必須 ● if err != nil { ... } とどちらが読みやすいか ○ Go wayからは外れる 31
Slide 32
Slide 32 text
夢: Webサーバーのパラメータ ● 入力値のスキーマをstruct型で表現 ● Hono/expressっぽいAPI ○ app.POST("/blog", handlePostBlog) ○ 第2引数をジェネリックにできる 32
Slide 33
Slide 33 text
夢: パイプライン演算子 ● g(f(x)) が x |> f |> g みたいに書けるやつ ○ Apply(x, f.Pipe(g)) ● かっこいい ○ 気をつけないとむしろ読みづらくなる ○ いいインタフェースにしないと破滅しそう 33
Slide 34
Slide 34 text
34 DEMO (時間あれば)
Slide 35
Slide 35 text
所感 (1) ● 関数がメソッドチェーンになることで見やす くなる (ことがある) ○ 濫用すると逆に見づらい 35
Slide 36
Slide 36 text
所感 (2) ● コンパイラの制約に阻まれることもある ○ Claude Codeにパーサコンビネータの実装を生成して もらったけどinstantiation cycleエラーでビルドが通 らなかった ○ Go 1.18から変わっていない制約 ■ サイズが無限に大きくなりうる型を定義できない 36
Slide 37
Slide 37 text
タイトルに答える ● 「メソッドのジェネリクスでGoの夢は広がる か?」 ○ 広がるところもある ○ メソッドのジェネリクス以外の制約に気をつける ○ 濫用するとどんどんGo wayから遠ざかる 37
Slide 38
Slide 38 text
まとめ ● Go 1.27でメソッドのジェネリクスが実装さ れる ○ これまではジェネリクスにできなかった ● いい使い方を思いついたら自慢してください ○ コンパイルエラーにならないなら 38
Slide 39
Slide 39 text
付録: デモ用のリポジトリ https://github.com/utgwkk/go127-generic s-dream 39
Slide 40
Slide 40 text
付録: VSCodeでHEADのgoを使う (1) $ go install golang.org/dl/gotip@latest $ gotip download $ gotip install golang.org/dl/gotip@latest 40
Slide 41
Slide 41 text
付録: VSCodeでHEADのgoを使う (2) .vscode/settings.json に追記する { "go.alternateTools": { "go": "~/sdk/gotip/bin/go", "gopls": "~/go/bin/gopls" } } 41