Go语言实现迭代器应按场景选机制:闭包适合轻量顺序遍历,channel适合并发流式消费;闭包需在工厂内声明状态变量并先增后判界,channel需协程内关闭且避免大缓冲。

Go 语言没有传统迭代器接口,也不支持用户自定义类型直接 for range,所以“实现迭代器”本质是选对场景、用对机制——不是硬套 Java 风格的 HasNext()/Next() 接口,而是根据需求选闭包、channel 或 iter.Seq。
闭包工厂函数:最轻量、最常用的做法
适合一次性顺序遍历、隐藏切片/数组内部结构、无需并发安全的场景。核心是返回一个捕获了索引状态的无参函数,每次调用返回 (T, bool)。
- 状态变量(如
i := -1)必须在工厂函数内声明,不能放在外层或结构体里,否则多个迭代器实例会共享状态 - 返回函数中要先
i++再判断越界,否则第一次调用就可能漏掉首元素 - 不要在闭包里做耗时操作(如 DB 查询),因为调用方可能只消费前几个值就
break - 示例:
func IntSliceIter(nums []int) func() (int, bool) { i := -1 return func() (int, bool) { i++ if i >= len(nums) { return 0, false } return nums[i], true } }
channel 实现:需要并发、可中断、流式消费时的首选
当你需要从 goroutine 异步生产数据、支持 context.Context 取消、或与其他 channel 组合(如 select)时,channel 是最 Go 的选择。
- 务必在 goroutine 内部处理错误并关闭 channel,否则接收方会永久阻塞或 panic
- 避免使用大缓冲 channel(如
make(chan int, 1000)),内存浪费且掩盖背压问题;通常make(chan T, 0)或make(chan T, 1)足够 - 接收方若需提前退出,应配合
select+ctx.Done(),不能只靠break,否则 goroutine 可能泄漏 - 示例:
func IntChan(nums []int)
iter.Seq[T]:Go 1.23+ 唯一官方认可、能直接 for range 的方案
仅在你需要统一抽象多种数据源(如 slice + file lines + DB rows)、或封装延迟计算逻辑时才用。普通切片遍历请直接 for range nums,别包装。
- 签名必须严格为
func(yield func(T) bool) error,返回值类型错成bool或nil会导致cannot range over ...编译错误 -
yield(v)返回false表示消费者中断(如break),此时你必须立即break循环,否则后续逻辑仍会执行 - 如果底层数据可能被并发修改(如全局变量),yield 前需加锁或复制快照,因为
yield函数可能被多次并发调用 - 示例:
func SliceSeq[T any](s []T) iter.Seq[T] { return func(yield func(T) bool) error { for _, v := range s { if !yield(v) { break // 必须加!否则 continue 执行可能浪费资源 } } return nil } }
最容易被忽略的一点:无论用哪种方式,都别为了“模式”而模式。Go 的惯用法是优先用 for range 直接遍历内建类型;只有当你要隐藏结构、解耦遍历逻辑、或对接外部流式数据时,才引入闭包、channel 或 iter.Seq。强行给每个 slice 包一层 Iterator 接口,反而增加维护成本和理解负担。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











