---
title: "Goのイテレータとは｜iterパッケージとrange over funcの仕組み・slices.Chunkの使い方"
url: "https://www.issoh.co.jp/tech/details/2829/"
published: 2024-06-27
updated: 2026-08-02
categories: ["Go"]
publisher: "株式会社一創"
---

# Goのイテレータとは｜iterパッケージとrange over funcの仕組み・slices.Chunkの使い方

Goのイテレータは、Go 1.23.0（2024年8月13日リリース）で言語仕様と標準ライブラリの両方に正式に入った機能です。`for range` に関数を渡せるようになり、スライスやマップ以外のデータ構造も同じ書き方でたどれるようになりました。この記事では、range over funcの動作規則、`iter` パッケージの `Seq`／`Pull`、スライスを一定サイズに切り出す `slices.Chunk` の挙動を、公式ドキュメントと標準ライブラリのソースに当たりながら整理します。

## まとめ

- イテレータの正体は `func(yield func(V) bool)` という関数です。`for range` はこの関数を1回だけ呼び、`yield` が呼ばれた回数だけループ本体が回ります。
- `yield` の戻り値がループ制御そのものです。ループ本体が `break` などで終了すると `yield` は `false` を返し、以後の呼び出しはpanicします。
- `slices.Chunk` の戻り値は `[][]T` ではなく `iter.Seq[Slice]` です。スライスとしてまとめて欲しい場合は `slices.Collect` と組み合わせます。
- 標準ライブラリのイテレータ対応は Go 1.23 の `slices`／`maps` から始まり、1.24 の `strings`／`bytes`、1.26 の `reflect` へと段階的に広がりました。
- `Map` や `Filter` といった汎用アダプタを標準で用意する提案（x/exp/xiter）は2025年5月21日に却下されています。当面は自分で書くか、素直な `for` ループを選ぶ判断になります。

英語圏の資料では range over func や Go iterator と表記され、検索するときは golang iter といった綴りでも同じ内容にたどり着けます。以下、仕様と実際のコードで順に確認します。

## range over funcでfor-rangeに渡せるイテレータの仕組み

Go 1.23の言語仕様変更は「`for range` の右辺に関数を書ける」という一点に集約されます。イテレータ用の新しい構文やキーワードは追加されていません。

### for-rangeが受け取る3つの関数型

言語仕様が受け付ける関数型は次の3つだけです。それ以外のシグネチャを渡すとコンパイルエラーになります。

| 関数型                   | ループ変数 | 対応する標準API例                |
| --------------------- | ----- | ------------------------- |
| func(func() bool)     | なし    | （自作の無限列など）                |
| func(func(V) bool)    | 1つ    | slices.Values / maps.Keys |
| func(func(K, V) bool) | 2つ    | slices.All / maps.All     |

コールバック引数は慣習的に `yield` と名付けますが、これは予約語ではなく通常の識別子です。次のコードは3から1まで降順に値を渡す最小のイテレータです。

```
package main

import "fmt"

// Countdown は n から 1 まで降順に値を渡すイテレータを返します。
func Countdown(n int) func(func(int) bool) {
	return func(yield func(int) bool) {
		for i := n; i >= 1; i-- {
			if !yield(i) {
				return
			}
		}
	}
}

func main() {
	for v := range Countdown(3) {
		fmt.Println(v)
	}
}
```

### yieldの戻り値とループ制御の対応

ここがrange over funcで最もつまずきやすい部分です。言語仕様は「ループ本体が（`break` 文などで）終了した場合、`yield` は `false` を返し、二度と呼び出してはならない」と定めています。`iter` パッケージのドキュメントはさらに踏み込んで「`yield` は `false` を返した後に呼ばれるとpanicする」と明記しています。

つまり `if !yield(i) { return }` は行儀の良い書き方ではなく、省略するとランタイムpanicを招く必須のガードです。先ほどの `Countdown` の呼び出し側に `break` を足しても安全に止まるのは、このガードがあるからです。

```
for v := range Countdown(3) {
	fmt.Println(v)
	if v == 2 {
		break // ここで yield が false を返し、Countdown 側の return に入る
	}
}
// 出力: 3, 2
```

ループ本体の `return` や `goto` でも同じく `yield` は `false` を返します。イテレータ関数の側から見ると、外側の制御構文は「`yield` の戻り値」という形でしか観測できません。

### Go 1.22の試験提供から1.23の正式化までの経緯

この機能はGo 1.22.0（2024年2月6日リリース）で先行提供されており、リリースノートには「`GOEXPERIMENT=rangefunc` を付けてビルドするとこの機能が有効になる」と書かれていました。6か月後の Go 1.23.0 で環境変数なしの標準機能に昇格しています。

したがって、range over funcを使うコードは `go.mod` の `go` ディレクティブが 1.23 以上である必要があります。プロジェクトごとにGoのバージョンを切り替えたい場合は、[GOTOOLCHAINによるGoバージョンの自動切り替え](/tech/details/5635/)を使うと `go.mod` の記述に応じたツールチェーンが自動で選ばれます。

## iterパッケージのSeq・Seq2とプル型イテレータ

`iter` パッケージ自体は非常に小さく、2つの型と2つの関数しか公開していません。言語仕様が受け付ける関数型に読みやすい名前を与え、プル型への変換手段を1つ提供するだけの薄い層です。

### Seq・Seq2の型定義と命名規約

型定義はそのまま、前節の関数型のエイリアスです。

```
type Seq[V any]     func(yield func(V) bool)
type Seq2[K, V any] func(yield func(K, V) bool)
```

自作の型でイテレータを提供するときは、標準ライブラリが採用した命名にそろえると読み手が迷いません。`All` は全要素（`Seq2` でインデックスや鍵と組にする）、`Values` は値のみ、`Backward`（`slices` のみに存在）は逆順、というのが実際に使われている割り当てです。Go公式ブログ「Range Over Function Types」（Ian Lance Taylor、2024年8月20日）は、コンテナ型には `All` メソッドを用意することを推奨しています。

```
import "iter"

// Values は s の要素を順に渡すイテレータを返します。
func Values[E any](s []E) iter.Seq[E] {
	return func(yield func(E) bool) {
		for _, v := range s {
			if !yield(v) {
				return
			}
		}
	}
}
```

### iter.Pullによる値の取り出しとstopの呼び出し義務

ここまでのイテレータは、値を受け手に押し付けるプッシュ型です。2つの列を突き合わせるような処理では、必要なタイミングで1件ずつ引き出すプル型のほうが書きやすくなります。`iter.Pull` はプッシュ型をプル型に変換します（以下は関数内に置く前提の抜粋です）。

```
next, stop := iter.Pull(slices.Values([]int{10, 20, 30}))
defer stop()

for {
	v, ok := next()
	if !ok {
		break
	}
	fmt.Println(v)
}
```

`stop` の呼び出しは省略できません。公式ドキュメントは「次の値が不要になり、かつ `next` がまだ終端（`false`）を返していない時点で `stop` を呼ばなければならない。通常は `defer stop()` とするとよい」としています。`stop` は複数回呼んでも、`next` が既に `false` を返した後に呼んでも安全です。

実装を知っておくと挙動を読み違えずに済みます。標準ライブラリのソース（src/iter/iter.go）は `runtime.newcoro` と `runtime.coroswitch` を `linkname` で参照しています。`newcoro` はgoroutineを1本生成しますが（src/runtime/coro.go のコメントは「fの実行を待って停止しているgoroutineを含むcoroを作る」と説明しています）、スケジューラを経由せず `coroswitch` で制御を直接受け渡すため、両者が並行に走ることはありません。ドキュメントも「複数のgoroutineから `next` や `stop` を同時に呼ぶのは誤り」と明記しているので、[ゴルーチンによる並行処理](/tech/details/4632/)と組み合わせる場合は呼び出しを1つのgoroutineに閉じ込めてください。

## slices.Chunkによるスライス分割の実装と落とし穴

`slices.Chunk` はGo 1.23.0で追加された関数で、スライスを指定サイズごとの連続した部分スライスに切り出します。バルクAPIへの分割送信やページング処理のように、一定件数ずつまとめて処理したい場面で使います。

### 戻り値は\[\]\[\]Tではなくiter.Seq

シグネチャは次のとおりです。

```
func Chunk[Slice ~[]E, E any](s Slice, n int) iter.Seq[Slice]
```

返るのはイテレータであって `[][]T` ではありません。`fmt.Println(slices.Chunk(ids, 3))` と書いても関数値のアドレスが出るだけで、コンパイルエラーにもならないため気づきにくい取り違えです。実際の使い方を確認しておきます。

```
package main

import (
	"fmt"
	"slices"
)

func main() {
	ids := []int{1, 2, 3, 4, 5, 6, 7}

	for c := range slices.Chunk(ids, 3) {
		fmt.Println(c)
	}
	// 出力: [1 2 3] 、[4 5 6] 、[7]

	// まとめて [][]int が欲しい場合は Collect と組み合わせます
	all := slices.Collect(slices.Chunk(ids, 3))
	fmt.Println(len(all)) // 3
}
```

### nの下限と端数チャンクの挙動

公式ドキュメントは「`n` が1未満の場合 `Chunk` はpanicする」と定めています。分割サイズを設定値や外部入力から受け取る設計では、呼び出し前の検証が必須です。実装（src/slices/iter.go）でも関数の冒頭で `panic("cannot be less than 1")` が実行されます。

要素数が `n` で割り切れない場合、最後のチャンクだけが短くなります。元のスライスが空のときは、空のチャンクが1つ返るのではなく、シーケンス自体が空です。つまりループ本体が一度も実行されません。0件の入力を正常系として扱うのか異常として扱うのかを、[Go言語のエラーハンドリング](/tech/details/3895/)の方針に沿って呼び出し側で明示しておかないと、後続処理が無言でスキップされる検知しにくい不具合になります。

### 元スライスとの配列共有によるバッチ処理の注意点

ドキュメントの「すべての部分スライスは長さを超える容量を持たないよう切り詰められる」という記述は、実装を見ると意味がはっきりします。`Chunk` は `yield(s[i : i+end : i+end])` という3インデックススライスを渡しており、容量を長さと同じに詰めています。

この結果、チャンクに `append` しても新しい配列が確保されるため元のスライスは汚れません。一方で、チャンクの**要素そのものを書き換えると元のスライスに波及します**。同じ配列を指しているためです。

```
ids := []int{1, 2, 3, 4}

for c := range slices.Chunk(ids, 2) {
	c[0] = 0          // 元の ids も書き換わります
	_ = append(c, 99) // cap が詰めてあるため ids には影響しません
}

fmt.Println(ids) // [0 2 0 4]
```

チャンクを別のgoroutineへ渡して並行に加工するようなバッチ処理では、この共有が競合の原因になります。書き換えを伴う場合は `slices.Clone` で複製してから渡してください。

## 標準ライブラリのイテレータ対応の広がり

Go 1.23の追加分だけを見ていると取りこぼしますが、標準ライブラリ側のイテレータ対応はその後も版を追って拡張されています。どのバージョンで何が使えるかを整理しておきます。

| バージョン | リリース日      | イテレータ関連の追加                    |
| ----- | ---------- | ----------------------------- |
| 1.22  | 2024-02-06 | GOEXPERIMENT=rangefunc で先行提供  |
| 1.23  | 2024-08-13 | iter新設、slices 9関数、maps 5関数    |
| 1.24  | 2025-02-11 | strings・bytes に各5関数           |
| 1.25  | 2025-08-12 | リリースノートに記載なし                  |
| 1.26  | 2026-02-10 | reflect の Type.Fields ほか6メソッド |

Go 1.24で `strings` と `bytes` に加わった `Lines`、`SplitSeq`、`SplitAfterSeq`、`FieldsSeq`、`FieldsFuncSeq` は、実務での効き目が大きい追加です。従来の `strings.Split` は結果のスライスを一括で確保しますが、`SplitSeq` は必要な分だけ返すため、巨大なテキストを行単位で処理する場面で確保するメモリを抑えられます。Go 1.26では `reflect.Type.Fields`／`Type.Methods`／`Type.Ins`／`Type.Outs` と `Value.Fields`／`Value.Methods` が追加され、`NumField` と添字ループで書いていたリフレクション処理が `for range` で書けるようになりました。なお本稿執筆時点（2026年8月）の安定版は go1.26.5 で、Go 1.27 は go1.27rc2 のリリース候補段階です。`iter` パッケージ自体への関数追加は1.27でも行われていません。最新版の確認方法と移行手順は[Go 1.27の新機能・変更点まとめ](/tech/details/10416/)で扱っています。

## x/exp/xiter却下後のMap・Filterの書き方

イテレータが入った直後から、`Map`／`Filter`／`Limit` といった汎用アダプタを標準で提供する提案（golang/go#61898「x/exp/xiter: new package with iterator adapters」）が議論されてきました。この提案は**2025年5月21日に提案審査グループによって declined as retracted（GitHub上のステータスは not planned）としてクローズされています**。`x/exp/xiter` というパッケージは公開されておらず、pkg.go.devでも404になります。

クローズ時のコメントでaclements氏は「Goにおけるイテレータはまだ日が浅く、経験を積む余地が大きい。特定のアダプタが広く使われるようになったら、個別の提案を出してほしい」と述べています。さらに関連issue #76481では、この提案と重なる新規提案はクローズから2年間（2027年5月まで）は歓迎されず、個人の経験談ではなくエコシステム全体の利用統計を添えることが期待される、という補足が示されました。

実務上の結論ははっきりしています。**標準の汎用アダプタを待つ設計にしてはいけません。**2〜3行で済む変換なら、自前のヘルパーを1つ定義するのが最も摩擦が少ない選択です。

```
// Map は seq の各要素に f を適用したイテレータを返します。
func Map[A, B any](seq iter.Seq[A], f func(A) B) iter.Seq[B] {
	return func(yield func(B) bool) {
		for v := range seq {
			if !yield(f(v)) {
				return
			}
		}
	}
}
```

アダプタを何段も重ねるパイプラインを組みたい場合は、標準採用の見込みが立っていない以上、サードパーティ製ライブラリへの依存になります。共通ヘルパーをどこに置くかは[Standard Project Layoutに沿ったディレクトリ構成](/tech/details/4285/)の判断と併せて決めておくと、後からの移動を避けられます。

## イテレータを書かないほうがよい場面

新機能は「使えるところすべてで使う」対象になりがちですが、この件については設計者自身が抑制的です。Go公式ブログでIan Lance Taylor氏は、イテレータで書ける処理について「多くの場合はループのほうが分かりやすい。全員がいつもこのスタイルでコードを書くよう促したいわけではない」と書いています。

実際、次の条件に当てはまるならイテレータ化は見送るべきです。

- 対象が既にスライスかマップである場合。`for i, v := range s` で足りるところを `slices.All` に置き換えても、読み手が追う関数が1つ増えるだけです。
- 反復のたびにエラーを返しうる場合。`iter.Seq2[T, error]` は書けますが、呼び出し側で毎回エラーを判定する分岐が入り、通常の `for` ループより冗長になります。#61898の議論でも、この形の扱いにくさが繰り返し指摘されました。
- 反復の途中で外部から進行を制御したい場合。プッシュ型のイテレータは制御を関数側に渡すため、`iter.Pull` を挟む一手間が発生します。

逆に効果がはっきりするのは、木構造やリンクリストのように「たどり方をカプセル化したいが、中間スライスを作りたくない」ケース、そして `slices.Chunk` や `strings.SplitSeq` のように**全体を確保せず必要な分だけ切り出す**ケースです。イテレータの価値は抽象化そのものではなく、この確保の回避にあります。

## よくある質問

### Goのイテレータはどのバージョンから使えますか

環境変数なしで使えるのはGo 1.23.0（go1.23、2024年8月13日リリース）以降です。Go 1.22.0では `GOEXPERIMENT=rangefunc` を付けてビルドした場合に限り利用できました。実際に使うには `go.mod` の `go` ディレクティブを 1.23 以上にする必要があり、これより古い指定のままだと range over func はコンパイルエラーになります。CI環境とローカルでツールチェーンがずれやすい箇所なので、GOTOOLCHAINの設定も併せて確認してください。

### slices.Chunkの結果を\[\]\[\]Tとして受け取るにはどうしますか

`slices.Collect(slices.Chunk(s, n))` と組み合わせます。`Chunk` の戻り値は `iter.Seq[Slice]` なので、そのまま `[][]T` の変数へ代入することはできません。ただし `Collect` を挟むと全チャンクを一度にメモリへ確保するため、確保量を抑えるというイテレータ本来の利点が失われます。バルクAPIへの分割送信のように1チャンクずつ処理して捨てられる用途では、`Collect` を挟まず `for range` で回すほうが適しています。

### breakでループを抜けた後にyieldを呼ぶとどうなりますか

panicします。言語仕様は「ループ本体が終了した場合、`yield` は `false` を返し、二度と呼び出してはならない」と定めており、`iter` パッケージのドキュメントも「`false` を返した後に呼ばれるとpanicする」と明記しています。イテレータ関数側では `if !yield(v) { return }` の形で必ず戻り値を判定してください。判定を省いたイテレータは、呼び出し側が `break` しない限り正常に動くため、テストをすり抜けやすい不具合になります。

### iter.Pullでstopを呼び忘れるとどうなりますか

反復が終端まで進んでいない状態で `stop` を呼ばずに抜けると、中断されたコルーチンとその実体であるgoroutineが解放されないまま残ります。`next` が `false` を返し切っていれば解放されますが、途中で `break` や `return` する可能性がある以上、`defer stop()` を常に書くのが安全です。`stop` は複数回呼んでも、`next` が既に `false` を返した後に呼んでも問題ないよう設計されています。

### MapやFilterは標準ライブラリにありますか

ありません。それらを提供する `x/exp/xiter` の提案は2025年5月21日にクローズされ、重なる新規提案も2027年5月までは受け付けない方針が示されています。go1.27rc2 時点でも `iter` パッケージが公開しているのは `Seq`／`Seq2` の2型と `Pull`／`Pull2` の2関数だけです。必要な変換は自前のヘルパー関数として定義するか、素直な `for` ループで書いてください。

## 関連記事

- [Go 1.27の新機能・変更点まとめ｜最新バージョンの確認方法と移行ガイド](/tech/details/10416/)
- [GOTOOLCHAINとは｜Go Toolchainでバージョンを自動切り替えする仕組みと設定方法](/tech/details/5635/)
- [ゴルーチン（Goroutine）とは？Go言語の並行処理を支える軽量スレッドの仕組みと使い方](/tech/details/4632/)
- [Go言語におけるエラーハンドリングの基本概念と重要性](/tech/details/3895/)
- [Go言語のディレクトリ構成のガイドライン「Standard Project Layout」を徹底解説](/tech/details/4285/)

---

出典: [Goのイテレータとは｜iterパッケージとrange over funcの仕組み・slices.Chunkの使い方](<https://www.issoh.co.jp/tech/details/2829/>)（株式会社一創）
