Go 1.22+ 的 func() iter.Seq[T] 生成器函数需返回 func(yield func(T) bool) error,通过闭包调用 yield 推送值,yield 返回 false 时立即退出;不可在内部启动 goroutine 或 channel,须保持同步拉取语义。

Go 1.22+ 的 func() iter.Seq[T] 生成器函数怎么写
Go 1.22 引入了原生迭代器支持,但不是靠「生成器函数」语法糖,而是通过 iter.Seq[T] 类型配合通道或闭包实现。你不能像 Python 那样用 yield,必须手动构造一个返回 func(yield func(T) bool) error 的函数。
典型写法是封装一个闭包,内部调用 yield 逐个推送值;一旦 yield(val) 返回 false(说明消费者中断),就立即退出。
-
iter.Seq[T]本质是「拉取式」协议,不启动 goroutine,也不涉及通道缓冲——它比通道更轻量、无调度开销 - 不要试图在
iter.Seq内部启动 goroutine + channel 往外推数据,这会破坏「同步拉取」语义,且 yield 可能被多次调用或跳过 - 若需异步生产(比如读文件、查 DB),应先用通道收拢数据,再用
iter.Seq包装通道消费逻辑——但注意:这会引入额外 goroutine 和内存分配
用 chan T 实现迭代器时为什么总卡死或漏数据
直接把 chan T 当作迭代器用,最常见问题是没关通道或没处理接收端提前退出。Go 的 range 遍历通道会阻塞等待,直到通道关闭;而消费者可能中途 break,导致生产者 goroutine 永久挂起。
- 务必在生产逻辑结束后调用
close(ch),否则range ch永远等不到 EOF - 不要在生产者里用
for range ch——那是消费,不是生产;生产者应只做ch - 若消费者用
for v := range ch,它不会感知到「上游已无新数据但尚未 close」,只能等 close 或 panic - 想支持「可中断」的通道迭代,得用带超时的
select+done通道,或改用iter.Seq
iter.Seq[T] 和 chan T 在性能与适用场景上的关键差异
二者根本不是替代关系:iter.Seq[T] 是接口协议,chan T 是并发原语。选哪个取决于你是否需要「并发安全的多消费者」或「流控能力」。
立即学习“go语言免费学习笔记(深入)”;
- 单次遍历、无并发消费 → 优先用
iter.Seq:零分配、无 goroutine、内联友好 - 需多个 goroutine 同时读同一数据源(如广播)→ 必须用
chan T(或sync.Map+ 锁) - 要背压(backpressure):通道天然支持缓冲区控制;
iter.Seq完全依赖调用方节奏,无法反向通知生产者慢点发 - 错误传递:通道只能传值,错误需额外返回或 panic;
iter.Seq的yield函数签名允许返回error,但实际很少用——通常错误应在构造iter.Seq时就处理好
从切片生成 iter.Seq[T] 的最简写法
这是高频需求,别写一堆闭包嵌套。Go 标准库 iter 包没提供 SeqOf,但一行就能搞定:
func SliceSeq[T any](s []T) iter.Seq[T] {
return func(yield func(T) bool) error {
for _, v := range s {
if !yield(v) {
break
}
}
return nil
}
}用法:for v := range SliceSeq([]int{1,2,3}) { ... }。注意:yield 返回 false 时循环立刻终止,不继续遍历剩余元素——这点和 range 一样,但可控性更强。
容易忽略的是:如果 T 是指针或大结构体,range s 会复制元素;需要避免复制时,得传 []*T 或用索引访问。


















