Go无内置迭代器接口,需手动实现Iterator结构体,典型模式是集合提供Iter()方法返回含状态的迭代器,Next()返回bool并推进状态,Value()安全获取值;推荐用泛型封装但须防零值陷阱;简单遍历优先用ForEach闭包。

Go 没有内置迭代器接口,得自己定义 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()跳过首元素或 panic -
Next()在已到末尾后继续调用,未检查边界,引发数组越界或空指针解引用 - 多个 goroutine 并发使用同一个
Iterator实例,而它非线程安全(绝大多数手写迭代器都不加锁)
推荐初始化时统一设游标为 -1,Next() 第一次调用才移到索引 0,这样能自然区分“未开始”和“已结束”两种状态。
立即学习“go语言免费学习笔记(深入)”;
用泛型写可复用的 Iterator[T],但注意零值陷阱
Go 1.18+ 可以用泛型封装通用逻辑,例如:
触网万能商城系统,3年专注打磨一款产品,专为网络服务公司、建站公司、威客、站长、设计师、网络运营及营销人员打造,是一款超级万能建站利器,彻底告别代码编程和找模板,改模板,改代码的低效高成本方式,仅需一个人可服务无数客户,系统集万能官网+万能商城+万能表单+博客+新闻+分销...于一体,通过海量模块拖拽布局、万能组合和超级自定义功能,可以构建各种类型的响应式网站。
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()就 panic - 零值
var 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。
真正难的不是写出来,而是决定什么时候不该写。









