Go无内置迭代器接口,需手动实现Iterator结构体,典型模式是集合提供Iter()方法返回含状态的迭代器,Next()返回bool并推进状态,Value()安全获取值;推荐用泛型封装但须防零值陷阱;简单遍历优先用ForEach闭包。
Iterator 类型Go 语言不提供类似 Java 的 Iterable 或 Python 的 __iter__ 机制,也没有泛型约束下的标准迭代器接口(直到 Go 1.18 泛型落地后仍无官方 Iterator 接口)。所以遍历自定义集合时,必须显式设计并实现自己的 Iterator 结构体和方法。
典型做法是让集合类型提供一个返回 Iterator 实例的方法
(比如 Iter()),而 Iterator 自身包含状态(如当前索引、指针或游标),并暴露 Next() 和 Value()(或合并为 Next() bool + 字段访问)。
Next() 返回 bool 表示是否还有元素,同时推进内部状态it.Value)或单独方法(如 it.Value())获取,避免多次调用开销Next() 中返回值元组(如 (T, bool))——Go 社区普遍认为这会增加调用方解构负担Iterator 状态管理容易出错:越界、重复读、未初始化常见错误不是逻辑写错,而是状态没管好。比如:
Iterator 后未将游标置为起始位置(如 -1 或 0),导致首次 Next() 跳过首元素或 panicNext() 在已到末尾后继续调用,未检查边界,引发数组越界或空指针解引用Iterator 实例,而它非线程安全(绝大多数手写迭代器都不加锁)推荐初始化时统一设游标为 -1,Next() 第一次调用才移到索引 0,这样能自然区分“未开始”和“已结束”两种状态。
Iterator[T],但注意零值陷阱Go 1.18+ 可以用泛型封装通用逻辑,例如:
type Iterator[T any] struct {
data []T
idx int
}
func (it *Iterator[T]) Next() bool {
it.idx++
return it.idx < len(it.data)
}
func (it *Iterator[T]) Value() T {
if it.idx < 0 || it.idx >= len(it.data) {
var zero T
return zero
}
return it.data[it.idx]
}
func NewIterator[T any](data []T) *Iterator[T] {
return &Iterator[T]{data: data, idx: -1}
}
这里的关键细节:
Value() 必须手动处理越界,不能直接 return it.data[it.idx] —— 否则 Next() 返回 false 后再调 Value() 就 panicvar zero T 是安全的,但若 T 是指针或带字段的结构体,返回零值可能掩盖业务逻辑错误(比如误以为拿到了有效数据)Iterator 设计成值类型(即不用指针接收者),否则 Next() 修改的是副本,状态无法保留ForEach 更 Go-idiomatic很多 Go 项目其实不用显式 Iterator,而是提供 ForEach(func(T)) 方法:
func (c *MyCollection[T]) ForEach(f func(T)) {
for _, v := range c.items {
f(v)
}
}
这种方式更简洁、不易出错,也天然规避了状态管理问题。但它不支持中途退出(除非加额外控制参数)、无法反向遍历、也不能与其他迭代操作组合(比如跳过前 N 个)。如果你只需要简单遍历,优先选这个;只有需要细粒度控制(如按需拉取、与 channel 配合、实现 Take/Filter)时,才值得投入成本写完整 Iterator。
真正难的不是写出来,而是决定什么时候不该写。