Go 怎么让自定义集合支持 for range 遍历
如果一个集合把内部 map 或切片直接暴露出来,调用方很快就会依赖存储细节。更合适的做法是给集合提供一个返回 iter.Seq[T] 的 All 方法:集合自己决定怎样产出元素,调用方仍然可以写出熟悉的 for v := range set.All()。
在支持 range-over-function 的 Go 版本中,自定义集合只要返回iter.Seq[T],并在闭包中逐个调用yield,就能直接被for range遍历;当yield返回false时,集合应立即停止当前遍历。
iter.Seq[T]的形状是func(yield func(T) bool)。- 集合通常用
All() iter.Seq[T]隐藏内部存储。 - 调用方的
break会让后续yield返回false,闭包必须尊重这个信号。
先确认 iter.Seq 为什么能接上 for range
这不是给 for range 增加一个集合接口,而是利用 Go 1.23 引入的函数类型遍历能力。标准库 iter 把单值序列定义成:
type Seq[V any] func(yield func(V) bool)
也就是说,集合返回的不是已经准备好的切片,而是一个“接收回调并产生值”的函数。range 负责提供回调,集合负责在合适的时候调用它。这个协议同时保留了提前结束的语义,比让调用方读取内部 map 更适合做包级 API。
把集合遍历协议写成 iter.Seq
下面用泛型集合保存唯一值。示例只关心遍历协议,集合的新增和查询方法保持简单:
package main
import (
"fmt"
"iter"
)
// Set 保存不重复的元素,内部 map 不暴露给调用方。
type Set[T comparable] struct {
items map[T]struct{}
}
// NewSet 创建一个可以直接写入的集合。
func NewSet[T comparable]() *Set[T] {
return &Set[T]{items: make(map[T]struct{})}
}
// Add 把元素放入集合,重复值不会产生第二份数据。
func (s *Set[T]) Add(v T) {
s.items[v] = struct{}{}
}
// All 返回集合的单值迭代器,方法名表达“所有元素”。
func (s *Set[T]) All() iter.Seq[T] {
return func(yield func(T) bool) {
for v := range s.items {
// false 表示调用方不再需要后续元素。
if !yield(v) {
return
}
}
}
}
func main() {
set := NewSet[string]()
set.Add("go")
set.Add("range")
// 调用方不需要知道集合内部使用了 map。
for v := range set.All() {
fmt.Println(v)
}
}
All 每次调用都创建一个新的闭包,闭包再读取当前集合内容。调用方只依赖 iter.Seq[T],因此以后把内部 map 换成树或其他结构,遍历 API 仍然可以保持不变。

调用方用 for range 消费,并正确处理中途停止
遍历接口最容易遗漏的是提前停止。调用方发现目标值后可以直接 break,此时迭代器收到的 yield 结果会变成 false。因此 All 里的判断不能省略,否则集合仍可能继续做无意义的工作。
// Find 返回第一个满足条件的元素。
func Find[T comparable](set *Set[T], want T) (T, bool) {
// zero 用于没有命中时的返回值。
var zero T
for v := range set.All() {
if v == want {
return v, true
}
}
return zero, false
}
这里不需要自己调用 iter.Pull。只有当消费方不适合写成 range,需要逐个调用 next 时,才考虑把 push 风格的序列转换成 pull 风格,并用 defer stop() 收尾。

空集合、重复遍历和 API 命名怎么定
空集合不需要特殊分支:内部 range 不进入循环,序列自然结束。一个普通的 All 也应支持重复调用;如果底层是不可回退的文件流或网络流,就要在文档中明确标注“单次使用”,避免调用方误以为每次都能从头开始。
| 设计点 | 建议 | 原因 |
|---|---|---|
| 单值集合 | All() iter.Seq[T] | 与常见 range 写法一致 |
| 键值序列 | Scan() iter.Seq2[K, V] | 保留 key 和 value 的配对关系 |
| 多种顺序 | 用 Backward 等名字区分 | 把顺序写进 API,而不是藏在实现里 |
| 中途停止 | 检查 yield 的 bool | 让 break 真正停止底层遍历 |
相关问题
自定义集合一定要叫 All 吗?
不强制,但 All 是集合返回全部元素时的常用约定。若方法只返回城市、倒序元素或过滤结果,使用能表达序列内容的名字更清楚。
iter.Seq 可以修改集合元素吗?
它本身只提供值。需要在遍历中修改时,可以让迭代器返回位置对象,再由位置对象提供受控的修改方法。
为什么不直接返回切片?
切片会暴露快照、复制或内部存储的选择;iter.Seq 让集合保留遍历实现权,也能自然响应提前停止。
Python sqlite3 批量写入怎么控制提交和回滚
- 上一篇
- Python sqlite3 批量写入怎么控制提交和回滚
- 下一篇
- 人类基准反应测试测的是什么?反应时间计算、设备延迟与结果解读
-
- Golang · Go教程 | 21小时前 | 并发 · Go教程 · trace.NewTask go tool trace Go runtime/trace 执行跟踪 trace.WithRegion
- Go runtime/trace 怎样按任务标记跟踪区间
- 391浏览 收藏
-
- Golang · Go教程 | 22小时前 | flag · go · Go 命令行参数 flag.TextVar encoding.TextUnmarshaler
- Go flag.TextVar 怎样把自定义类型接入命令行
- 184浏览 收藏
-
- Golang · Go教程 | 23小时前 | 错误处理 · 模板 · Go教程 · html/template · 模板渲染 Go html/template missingkey 模板缺失字段 Execute错误
- Go html/template 缺失字段的严格渲染配置与错误处理
- 371浏览 收藏
-
- Golang · Go教程 | 23小时前 | html · go · 模板 · url · Go html/template 自动转义 URL上下文 template.URL
- Go html/template 怎样避免 URL 上下文误用
- 326浏览 收藏
-
- Golang · Go教程 | 23小时前 |
- Go text/template.ParseFS 怎样加载嵌入文件模板
- 208浏览 收藏
-
- Golang · Go教程 | 23小时前 |
- Go database/sql.NamedArg 怎样给 SQL 参数命名
- 311浏览 收藏
-
- Golang · Go教程 | 1天前 | HTTP · go · Gin · Gin ShouldBindBodyWith 请求体重复绑定 ShouldBind binding.JSON
- Gin ShouldBindBodyWith 怎么让同一个请求体绑定两次
- 134浏览 收藏
-
- Golang · Go教程 | 1天前 | go · 文件系统 · Go os.ReadDir Go File.ReadDir Go DirEntry Go 目录遍历
- Go os.ReadDir 怎样高效读取目录条目
- 269浏览 收藏
-
- Golang · Go教程 | 1天前 | go · Go go/types.Config Go Importer Go 类型检查 types.Check
- Go go/types.Config 怎样给类型检查接入导入器
- 222浏览 收藏
-
- Golang · Go教程 | 1天前 |
- Go maphash.Hash 怎样用固定种子复现哈希结果
- 341浏览 收藏
-
- Golang · Go教程 | 1天前 | Go 字节切片 encoding/binary AppendByteOrder
- Go binary.ByteOrder.Append 怎样把整数追加到现有字节切片
- 373浏览 收藏
-
- Golang · Go教程 | 1天前 | go · IPv6 Go ipv4 netip.ParsePrefix CIDR
- Go netip.ParsePrefix 怎样解析 CIDR 字符串
- 334浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 575次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 617次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 649次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 609次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 424次使用
-
- Go泛型实战教程之如何在结构体中使用泛型
- 2023-02-24 234浏览
-
- 关于go-zero单体服务使用泛型简化注册Handler路由的问题
- 2022-12-30 346浏览
-
- Go1.18新特性工作区模糊测试及泛型的使用详解
- 2022-12-30 131浏览
-
- Go泛型应用工厂方法及泛型使用
- 2022-12-27 185浏览
-
- Go1.18新特性使用Generics泛型进行流式处理
- 2022-12-24 265浏览

