Slide 1

Slide 1 text

range over func 2年間の軌跡 Issue #56413 はGoのエコシステムをどう変えたか 合同会社DMM.com / オンラインサロン開発部 國分竜二 2026 / 09 / 11 © DMM.com

Slide 2

Slide 2 text

自己紹介 ● 合同会社DMM.com オンライン サロン開発部 ● 今日ブースも出してるのでぜひ 遊びに来てください(もう終わり ですが...) ● X: @_ryuji_cre8ive © DMM.com

Slide 3

Slide 3 text

range over funcについての確認 会場の皆さんにアンケートを取りたいと思います © DMM.com

Slide 4

Slide 4 text

range over funcについての確認 会場の皆さんにアンケートを取りたいと思います 手を挙げて答えてください © DMM.com

Slide 5

Slide 5 text

range over funcについての確認 初めて聞いた🖐 © DMM.com

Slide 6

Slide 6 text

range over funcについての確認 知ってるよ🖐 © DMM.com

Slide 7

Slide 7 text

range over funcについての確認 使ってるよ🖐 © DMM.com

Slide 8

Slide 8 text

range over funcについての確認 ご協力ありがとうご ざいます🫶 © DMM.com

Slide 9

Slide 9 text

改めて復習 ● range over func とは? ● 関数値を range で反復する仕 組み ● Go 1.23 で正式導入 ● iter.Seq / iter.Seq2 を range で 反復 for v := range seq { use(v) } for k, v := range seq2 { use(k, v) } © DMM.com

Slide 10

Slide 10 text

閑話休題 © DMM.com

Slide 11

Slide 11 text

range over func 2年間の軌跡 Issue #56413 はGoのエコシステムをどう変えたか 合同会社DMM.com / オンラインサロン開発部 國分竜二 2026 / 09 / 11 © DMM.com

Slide 12

Slide 12 text

この3つ、なぜ全部シグネチャが違うんでしょう? filepath.Walk( func(path string, info fs.FileInfo, err error) error, ) sync.Map.Range( func(key, value any) bool, ) flag.Visit( func(f *flag.Flag), ) © DMM.com

Slide 13

Slide 13 text

この3つ、なぜ全部シグネチャが違うんでしょう? filepath.Walk( func(path string, info fs.FileInfo, err error) error, ) sync.Map.Range( func(key, value any) bool, ) flag.Visit( func(f *flag.Flag), ) © DMM.com どれもとあるコレクションを走 査するメソッドです

Slide 14

Slide 14 text

この3つ、なぜ全部シグネチャが違うんでしょう? filepath.Walk( func(path string, info fs.FileInfo, err error) error, ) やりたいことは同じ。 func(key, それなのに呼び出し方と戻り値はバラバラ value any) bool, sync.Map.Range( ) flag.Visit( func(f *flag.Flag), ) © DMM.com どれもとあるコレクションを走 査するメソッドです

Slide 15

Slide 15 text

CHAPTER 1 戦国時代 Why a common iteration protocol was needed © DMM.com

Slide 16

Slide 16 text

標準ライブラリの走査APIは、形が揃っていない 共通の interface はなく、呼び出し方も戻り値もバラバラ ● ● ● ● ● Frames.Next (T, bool) Scanner.Scan bool sql.Rows.Next bool + Err filepath.Walk(root, fn) sync.Map.Range(fn) © DMM.com

Slide 17

Slide 17 text

同じ (T, bool) でも意味が違う // 多くの慣習: bool は「Tが有効かどうか」 v, ok := m[key] // okがtrueならvは有効 // runtime.Frames.Next は違う frame, more := frames.Next() // more は「次の呼び出しが有効な値を返すか」 © DMM.com

Slide 18

Slide 18 text

同じ (T, bool) でも意味が違う // 多くの慣習: bool は「Tが有効かどうか」 v, ok := m[key] // okがtrueならvは有効 // runtime.Frames.Next は違う frame, more := frames.Next() // more は「次の呼び出しが有効な値を返すか」 同じ形をしていても、中身の意味は関数ごとに学び直す必要があった。 © DMM.com

Slide 19

Slide 19 text

2022年10月25日、議論が動き出す Discussion #56413 “user-defined iteration using range over func values” © DMM.com なぜ動き出したか

Slide 20

Slide 20 text

2022年10月25日、議論が動き出す Discussion #56413 なぜ動き出したか “user-defined iteration using range over func values” Goにはシーケンシャルなデータを反復処理する標 © DMM.com 準的な方法がなかった。各実装が個別最適化し た結果、Goユーザーのキャッチアップコストが増 大していた。

Slide 21

Slide 21 text

実際の議論を見てみる 「標準的な iteration がない」 という問題意識から range over func の議論が始 まりました © DMM.com

Slide 22

Slide 22 text

実験から正式リリースまでの時系列 2022.10 Discussion #56413 オープン © DMM.com → 2023.07 Issue #61405 提案提出 → Go 1.22 GOEXPERIMENT=rangefunc で試験導入 → Go 1.23 2024.08 正式リリース 言語機能として有効化

Slide 23

Slide 23 text

CHAPTER 2 設計の決断 Why a function type, not an interface © DMM.com

Slide 24

Slide 24 text

設計の決断 みなさんなら、 interfaceで解決しませんか? type Iterator[T any] interface { Next() (T, bool) } // でもGoチームはこの道を選ばなかった © DMM.com

Slide 25

Slide 25 text

設計の決断 みなさんなら、 interfaceで解決しませんか? type Iterator[T any] interface { Next() (T, bool) } // でもGoチームはこの道を選ばなかった 答えは、言語仕様そのものへの組み込みでした。 © DMM.com

Slide 26

Slide 26 text

Issue #61405 — rsc、2023年7月17日 “spec: add range over int, range over func” Discussion #56413 の議論を受けて、 for-range で整数と関数を扱えるようにする 言語拡張として提案が出された © DMM.com 11

Slide 27

Slide 27 text

Issue #61405 — rsc、2023年7月17日 “spec: add range over int, range over func” Discussion #56413 の議論を受けて、 for-range で整数と関数を扱えるようにする 言語拡張として提案が出された push-iteratorのみを言語サポートの 対象にし、pull-iteratorへの変換は 別の標準ライブラリ関数に委ねる、 という役割分担を明確化した。 出典 : golang/go©Issue #61405 DMM.com 11

Slide 28

Slide 28 text

Issue #61405 — rsc、2023年7月17日 “spec: add range over int, range over func” Push? Pull? Discussion #56413 の議論を受けて、 for-range で整数と関数を扱えるようにする 言語拡張として提案が出された push-iteratorのみを言語サポートの 対象にし、pull-iteratorへの変換は 別の標準ライブラリ関数に委ねる、 という役割分担を明確化した。 出典 : golang/go©Issue #61405 DMM.com 11

Slide 29

Slide 29 text

Push と Pull:誰が次の1件を主導するか Push Iterator © DMM.com

Slide 30

Slide 30 text

Push と Pull:誰が次の1件を主導するか Push Iterator yieldを呼び出し、値を mainにpush © DMM.com

Slide 31

Slide 31 text

Push と Pull:誰が次の1件を主導するか Pull Iterator Push Iterator yieldを呼び出し、値を mainにpush © DMM.com

Slide 32

Slide 32 text

Push と Pull:誰が次の1件を主導するか Pull Iterator Push Iterator yieldを呼び出し、値を mainにpush main側が明示的に 次の値を呼び出す next関数を実行 © DMM.com

Slide 33

Slide 33 text

Push と Pull:誰が次の1件を主導するか Pull Iterator Push Iterator Push / Pullは「値の向き」ではなく yieldを呼び出し、値を iterationの主導権がどちらにあるかを表します mainにpush main側が明示的に 次の値を呼び出す next関数を実行 © DMM.com

Slide 34

Slide 34 text

range over funcは、どうやって動く? “For a function f, the iteration proceeds by calling f with a synthesized yield function that invokes the body of the loop.” 関数 f の反復は、ループ本体を呼び出す合成された yield 関数を渡して、f を呼び出すことで進む。 © DMM.com

Slide 35

Slide 35 text

range over funcは、どうやって動く? “For a function f, the iteration proceeds by calling f with a synthesized yield function that invokes the body of the loop.” 関数 f の反復は、ループ本体を呼び出す合成された yield 関数を渡して、f を呼び出すことで進む。 コンパイラが yield関数を合成し、 range本体へ渡している。 © DMM.com

Slide 36

Slide 36 text

rangeの中のdeferは、いつ実行される? for v := range seq { defer cleanup(v) … } © DMM.com

Slide 37

Slide 37 text

rangeの中のdeferは、いつ実行される? for v := range seq { defer cleanup(v) … } © DMM.com 1回ごとにdefer?

Slide 38

Slide 38 text

rangeの中のdeferは、いつ実行される? for v := range seq { defer cleanup(v) … } 1回ごとにdefer? 答え:外側の関数が returnするとき。他の rangeループと同じ © DMM.com

Slide 39

Slide 39 text

CHAPTER 3 標準ライブラリの変化 What actually changed in the stdlib © DMM.com

Slide 40

Slide 40 text

ソート済みmap を走査する BEFORE © DMM.com

Slide 41

Slide 41 text

ソート済みmap を走査する BEFORE ①sに要素を append © DMM.com

Slide 42

Slide 42 text

ソート済みmap を走査する BEFORE ①sに要素を append ②配列sをソート © DMM.com

Slide 43

Slide 43 text

ソート済みmap を走査する BEFORE ①sに要素を append ②配列sをソート ③出力 © DMM.com

Slide 44

Slide 44 text

ソート済みmap を走査する BEFORE ①sに要素を append コードが長い ... ②配列sをソート ③出力 © DMM.com

Slide 45

Slide 45 text

ソート済みmap を走査する BEFORE © DMM.com AFTER

Slide 46

Slide 46 text

ソート済みmap を走査する AFTER BEFORE ①キーを返す iter func © DMM.com

Slide 47

Slide 47 text

ソート済みmap を走査する AFTER BEFORE ①キーを返す iter func ②iterを受け取りソー トされた配列で返す © DMM.com

Slide 48

Slide 48 text

ソート済みmap を走査する AFTER BEFORE ①キーを返す iter func ②iterを受け取りソー トされた配列で返す ③出力 © DMM.com

Slide 49

Slide 49 text

ソート済みmap を走査する AFTER BEFORE ①キーを返す iter func たった3行に...! ②iterを受け取りソー トされた配列で返す ③出力 © DMM.com

Slide 50

Slide 50 text

固定回数のループ・逆順走査 BEFORE for i := 0; i < n; i++ { /* ... */ } for i := n-1; i >= 0; i-- { _ = s[i] } © DMM.com

Slide 51

Slide 51 text

固定回数のループ・逆順走査 BEFORE for i := 0; i < n; i++ { /* ... */ } ①よくあるforループ for i := n-1; i >= 0; i-- { _ = s[i] } © DMM.com

Slide 52

Slide 52 text

固定回数のループ・逆順走査 BEFORE for i := 0; i < n; i++ { /* ... */ } ①よくあるforループ for i := n-1; i >= 0; i-- { _ = s[i] } © DMM.com ②逆順にした途端 あれ、条件は? イテレーションの向きは?

Slide 53

Slide 53 text

固定回数のループ・逆順走査 BEFORE for i := 0; i < n; i++ { /* ... */ } ①よくあるforループ パニック for i := n-1; i >= 0; i-- { _ = s[i] } © DMM.com ②逆順にした途端 あれ、条件は? イテレーションの向きは?

Slide 54

Slide 54 text

固定回数のループ・逆順走査 BEFORE AFTER for i := 0; i < n; i++ { /* ... */ } for i := range n { for i := n-1; i >= 0; i-- { _ = s[i] } for i, v := range © DMM.com // ... } slices.Backward(s) { // ... }

Slide 55

Slide 55 text

固定回数のループ・逆順走査 BEFORE AFTER for i := 0; i < 便利な関数 n; i++ { /* ... */ } Backward登場 for i := range n { for i := n-1; i >= 0; i-- { _ = s[i] } for i, v := range © DMM.com // ... } slices.Backward(s) { // ... }

Slide 56

Slide 56 text

固定回数のループ・逆順走査 BEFORE for i := 0; i < 便利な関数 n; i++ { /* ... */ } Backward登場 for i := n-1; i >= 0; i-- { _ = s[i] } © DMM.com AFTER for i := range n { // ... �� } for i, v := range slices.Backward(s) { // ... }

Slide 57

Slide 57 text

maps.Keys / Values は、なぜ iterator になった? 2021.07 #47330 maps 構想 Keys / Values →sliceを返す © DMM.com

Slide 58

Slide 58 text

maps.Keys / Values は、なぜ iterator になった? 2021.07 #47330 2022.12 #57436 maps 構想 Keys / Values →sliceを返す Go 1.21向けに x/exp/maps 標準化を提案 © DMM.com

Slide 59

Slide 59 text

maps.Keys / Values は、なぜ iterator になった? iter構想 2021.07 #47330 2022.12 #57436 maps 構想 Keys / Values →sliceを返す Go 1.21向けに x/exp/maps 標準化を提案 © DMM.com

Slide 60

Slide 60 text

Go 1.21でslice版を出すのは早い? Go 1.22でiteratorを返す可能性が高いなら、 Go 1.21でsliceを返す形で出すのは短絡的では ないか。 いったん取り下げるべきでは? 将来、iteratorとしてMap / Filter / Reduceと 組み合わせる可能性がある。 sliceが必要なら slices.Collect で変換できる。 © DMM.com

Slide 61

Slide 61 text

maps.Keys / Values は、なぜ iterator になった? iter構想 2021.07 #47330 2022.12 #57436 2023.07 #61538 maps 構想 Keys / Values →sliceを返す Go 1.21向けに x/exp/maps 標準化を提案 slice版を見送り © DMM.com KeysSlice案も 不採用#61626

Slide 62

Slide 62 text

maps.Keys / Values は、なぜ iterator になった? iter構想 2021.07 #47330 2022.12 #57436 2023.07 #61538 2023.08 #61900 maps 構想 Keys / Values →sliceを返す Go 1.21向けに x/exp/maps 標準化を提案 slice版を見送り iter.Seqを追加 Keys / Values Go 1.23 © DMM.com KeysSlice案も 不採用#61626

Slide 63

Slide 63 text

maps.Keys / Values は、なぜ iterator になった? iter構想 2021.07 #47330 2022.12 #57436 2023.07 #61538 2023.08 #61900 maps 構想 Keys / Values →sliceを返す Go 1.21向けに x/exp/maps 標準化を提案 slice版を見送り iter.Seqを追加 Keys / Values Go 1.23 KeysSlice案も 不採用#61626 共通のiteration protocolができたことで、 APIの戻り値だけでなく、名前の使い方にまで影響を及ぼした © DMM.com

Slide 64

Slide 64 text

パフォーマンスはどう変わったか(実測) 共通条件:1M items / Go 1.23.1 / M4 Pro 全件処理(時間 / メモリ/ 確保回数) 早期終了(時間 / メモリ/ 確保回数) Slice 63.1ms / 8.0MB / 1 alloc 6.78ms / 8.0MB / 1 alloc Iter 64.7ms / 41.7MB / 41 allocs 3.43ms / 0B / 0 allocs 指標 © DMM.com

Slide 65

Slide 65 text

パフォーマンスはどう変わったか(実測) 共通条件:1M items / Go 1.23.1 / M4 Pro 全件処理(時間 / メモリ/ 確保回数) 早期終了(時間 / メモリ/ 確保回数) Slice 63.1ms / 8.0MB / 1 alloc 6.78ms / 8.0MB / 1 alloc Iter 64.7ms / 41.7MB / 41 allocs 3.43ms / 0B / 0 allocs 指標 Iterはわずかに不 利 © DMM.com

Slide 66

Slide 66 text

パフォーマンスはどう変わったか(実測) 共通条件:1M items / Go 1.23.1 / M4 Pro 全件処理(時間 / メモリ/ 確保回数) 早期終了(時間 / メモリ/ 確保回数) Slice 63.1ms / 8.0MB / 1 alloc 6.78ms / 8.0MB / 1 alloc Iter 64.7ms / 41.7MB / 41 allocs 3.43ms / 0B / 0 allocs 指標 Iterはわずかに不 利 © DMM.com Iter が約2倍高速

Slide 67

Slide 67 text

パフォーマンスはどう変わったか(実測) 共通条件:1M items / Go 1.23.1 / M4 Pro 指標 Slice Iter 全件処理(時間 / メモリ/ 確保回数) 早期終了(時間 / メモリ/ 確保回数) 63.1ms / 8.0MB / 1 alloc 6.78ms / 8.0MB / 1 alloc 64.7ms / 41.7MB / 41 allocs 3.43ms / 0B / 0 allocs 早期終了できるようなケースでは 圧倒的に高パフォーマンス Iterはわずかに不 利 © DMM.com Iter が約2倍高速

Slide 68

Slide 68 text

iterator化されなかったAPIもある os.ReadDir / filepath.Glob すべての APIがiterator向き とは限らない © DMM.com なぜ? ● APIはソート済みの結果を返す仕様。 ● 全件を先に集める必要がある。 ● 早期終了とメモリ節約が効かない。

Slide 69

Slide 69 text

CHAPTER 4 OSSエコシステムの変化 How the community responded © DMM.com

Slide 70

Slide 70 text

xiterの提案は、どうなった? 2023.08 xiter 提案 ・Map ・Concat ・Equal …etc © DMM.com

Slide 71

Slide 71 text

xiterの提案は、どうなった? 2023.08 2024.07 xiter 提案 ・Map ・Concat ・Equal …etc Go 1.23対応 仕様に合わせ てxiter更新 © DMM.com

Slide 72

Slide 72 text

xiterの提案は、どうなった? 2023.08 2024.07 Go 1.23 xiter 提案 ・Map ・Concat ・Equal …etc Go 1.23対応 仕様に合わせ てxiter更新 iter、標準ライ ブラリへ © DMM.com

Slide 73

Slide 73 text

xiterの提案は、どうなった? 2023.08 2024.07 Go 1.23 xiter 提案 ・Map ・Concat ・Equal …etc Go 1.23対応 仕様に合わせ てxiter更新 iter、標準ライ ブラリへ © DMM.com 現在 not planned (お見送り)

Slide 74

Slide 74 text

xiterの提案は、どうなった? 2023.08 2024.07 Go 1.23 xiter 提案 ・Map ・Concat ・Equal …etc Go 1.23対応 仕様に合わせ てxiter更新 iter、標準ライ ブラリへ 現在 not planned (お見送り) 見送り理由 →標準に入れるほど共通の使い道がまだ見えていない © DMM.com

Slide 75

Slide 75 text

sqlc: iterator API は今も議論中 Seq + Err() ループ内の err は不要 © DMM.com Seq2[T, error]

Slide 76

Slide 76 text

sqlc: iterator API は今も議論中 Seq + Err() ループ内の err は不要 ループ内の err は不要 © DMM.com Seq2[T, error]

Slide 77

Slide 77 text

sqlc: iterator API は今も議論中 Seq + Err() ループ内の err は不要 ループ内の err は不要 © DMM.com Seq2[T, error]

Slide 78

Slide 78 text

sqlc: iterator API は今も議論中 Seq + Err() Seq2[T, error] ループ内の err は不要 ループ内の err は不要 iteration中のエラーは返せる ただし、break後のClose()エラーをどのように利 用側に返すかは別問題 © DMM.com

Slide 79

Slide 79 text

sqlc: iterの議論は終わらなかった 2024.10 #3631 iterator API Seq+Err vs Seq2 © DMM.com

Slide 80

Slide 80 text

sqlc: iterの議論は終わらなかった 2024.10 #3631 2025.09 #4108 iterator API Seq+Err vs Seq2 APIを決めず helperに限定 © DMM.com

Slide 81

Slide 81 text

sqlc: iterの議論は終わらなかった 2024.10 #3631 2025.09 #4108 2026.06 #4488 iterator API Seq+Err vs Seq2 APIを決めず helperに限定 Seq2を試す opt-in PoC © DMM.com

Slide 82

Slide 82 text

sqlc: iterの議論は終わらなかった 2024.10 #3631 2025.09 #4108 2026.06 #4488 iterator API Seq+Err vs Seq2 APIを決めず helperに限定 Seq2を試す opt-in PoC →sqlcのiterator APIはまだ決定版に至っていない。 range over funcで反復は共通化できても、共通の protocolだけでは、ドメイ ン固有のライフサイクル設計までは解決できない。 © DMM.com

Slide 83

Slide 83 text

実際に動いているOSS実装 go-simpler.org/queries // go-simpler.org/queries func Query[T any]( ctx context.Context, q Querier, query string, args ...any, ) iter.Seq2[T, error] for row, err := range queries.Query[Order](ctx, db, sql) { if err != nil { return err } } Close や defer を利用側が意識しなくていい設計に! © DMM.com

Slide 84

Slide 84 text

比較 go-simpler sqlc ライブラリが責任を引き受ける 生成APIとして契約を決める必要がある エラーは基本的に致命的。そこで停止 エラーの扱い方自体を検討中 Closeは利用者から隠す Closeやbreak時の扱いも設計対象 狭い用途に対して意見を決めやすい 多くの利用者・既存コードへの影響が大きい © DMM.com

Slide 85

Slide 85 text

比較 go-simpler sqlc ライブラリが責任を引き受ける 生成APIとして契約を決める必要がある Closeは利用者から隠す Closeやbreak時の扱いも設計対象 go-simpler:Closeとエラー処理をライブラリ 側で引き受ける設計を選んだ。 エラーは基本的に致命的。そこで停止 エラーの扱い方自体を検討中 sqlc:生成APIとしてどこまで利用者に委ね 狭い用途に対して意見を決めやすい 多くの利用者・既存コードへの影響が大きい るかを決める必要がある。 © DMM.com

Slide 86

Slide 86 text

CHAPTER 5 2年経った今、どう使う? From observation to your own code © DMM.com

Slide 87

Slide 87 text

[]T を返すか、iter.Seq を返すか 複数回イテレーションする → []T 全件が必要(ソート・集計など) → []T 1回限りのストリーム → iter.Seq 早期終了の効果が大きい → iter.Seq 置き換え関係ではない。呼び出し側の利用特性で選ぶ。 © DMM.com

Slide 88

Slide 88 text

今日の Before / After を、自分 のコードへ 観察者として追った2年間。次はあなたの番です。 © DMM.com

Slide 89

Slide 89 text

Thank you! ● ● ● ● ● Discussion #56413 github.com/golang/go/discussions/56413 Issue #61405 github.com/golang/go/issues/61405 Issue #61898 github.com/golang/go/issues/61898 sqlc PR #3631 github.com/sqlc-dev/sqlc/pull/3631 Go Wiki go.dev/wiki/RangefuncExperiment ご清聴ありがとうございました © DMM.com