Go cmp.Or 怎么从多个候选值中选择第一个非零值
直接说结论:Go 的 cmp.Or 会从左到右检查传入的候选值,返回第一个不等于该类型零值的值;如果所有候选都是零值,就返回该类型的零值。它特别适合表达“命令行参数优先,其次环境变量,最后内置默认值”这类简单的配置回退。
官方文档:https://pkg.go.dev/cmp
这个函数从 Go 1.22 开始提供,签名中的类型约束是 comparable,因此所有候选必须能使用等号比较,并且属于同一种可推断类型。

最小写法:把候选值按优先级排好
最常见的使用方式是把优先级最高的候选放在最前面,把兜底值放在最后。下面的例子中,命令行没有设置地址,环境变量提供了地址,因此最终选择环境变量的值。
package main
import (
"cmp"
"fmt"
)
func main() {
// 候选值按优先级从高到低排列
flagAddr := ""
envAddr := "10.0.0.8:8080"
defaultAddr := "127.0.0.1:8080"
addr := cmp.Or(flagAddr, envAddr, defaultAddr)
fmt.Println(addr)
}
10.0.0.8:8080
这里没有特殊的“默认值标记”。对字符串来说,空字符串就是零值,所以第一个候选被跳过;第二个候选非空,函数立即把它作为结果。
问题通常出在没有先想清楚“零值”
cmp.Or 判断的不是“这个值是否合理”,而是“这个值是否等于其类型的零值”。不同类型的零值不同:
| 类型示例 | 零值 | 使用时的含义 |
|---|---|---|
| string | 空字符串 | 常用于“未提供文本配置” |
| int、float64 | 0 | 只有业务上把 0 视为缺省时才适合 |
| bool | false | false 会被当成未选择,容易丢失显式关闭意图 |
| 指针 | nil | 可在多个可选对象之间取第一个非 nil 值 |
| 可比较结构体 | 各字段均为零值 | 整个结构体与其零值比较 |
因此,端口号用 0 表示“自动分配”时,不能再把 0 同时当作“未配置”。如果业务语义需要区分这两种状态,应增加显式的是否设置标志,或者使用指针、可选值结构。
动手验证:全部为零值时会发生什么
全部候选都是零值并不会报错。函数会返回同类型的零值;不传任何候选时也遵循这一规则,只要类型能够从上下文或显式类型实参中确定。
package main
import (
"cmp"
"fmt"
)
func main() {
// 三个候选都是字符串零值,因此结果仍为空字符串
text := cmp.Or("", "", "")
// 没有实参时显式指定类型,结果是 int 的零值 0
number := cmp.Or[int]()
fmt.Printf("text=%q number=%d\n", text, number)
}
text="" number=0
这也是为什么把兜底值放在最后很重要:只要兜底值本身非零,正常配置路径就总能得到一个明确结果。
类型限制:slice、map 和 function 不能直接传入
cmp.Or 的类型参数要求 comparable。字符串、数字、布尔值、指针、通道、接口,以及字段都可比较的数组和结构体可以使用;slice、map 和 function 不能作为普通值直接比较,因此不能直接作为候选传入。

如果要从多个切片中选择第一个非空切片,仍然需要显式判断长度。这样写也更准确,因为 nil 切片和长度为 0 的非 nil 切片在业务上是否等价,需要由调用方决定。
package main
import "fmt"
func firstNonEmpty[T any](candidates ...[]T) []T {
// slice 不满足 comparable,因此按长度显式判断
for _, candidate := range candidates {
if len(candidate) > 0 {
return candidate
}
}
return nil
}
func main() {
// 返回第一个长度大于 0 的切片
result := firstNonEmpty([]int{}, []int{3, 5}, []int{8})
fmt.Println(result)
}
[3 5]
两个容易忽略的边界
false 或 0 可能是有效输入
如果用户明确填写 false 表示关闭功能,使用 cmp.Or 会继续选择后面的 true,从而改变用户意图。同理,0 如果代表“不限速”“自动端口”或某个有效枚举,也不应被无条件跳过。此时应该保存“是否设置”的独立状态,而不是只看值本身。
它不是惰性求值工具
调用函数前,Go 会先求出每个实参的值。因此即使第一个候选已经非零,写在后面的函数调用也已经执行。若候选计算昂贵、可能失败或带副作用,应继续使用 if 判断,让后续计算只在确实需要时发生。
func loadValue(primary string) string {
if primary != "" {
// 已有主值时直接返回,避免继续执行昂贵查询
return primary
}
return queryRemoteFallback()
}
速查:什么时候值得用 cmp.Or
- 候选值类型相同,并且满足 comparable 约束。
- 该类型的零值在业务上确实表示“未提供”。
- 候选值已经准备好,不依赖惰性计算或错误处理。
- 优先级可以清楚地用从左到右的参数顺序表达。
- 最后最好提供一个非零兜底值,避免结果仍是零值。
总结:把 cmp.Or 理解成“按顺序选择第一个非零值”就够了。它能明显压缩简单的默认值选择代码,但不会判断业务有效性,也不会替你区分 false、0 与“未设置”。先确认零值语义,再决定是否用它。
相关问题
cmp.Or 可以混用 string 和自定义字符串类型吗?
一次调用中的候选必须能推断成同一个类型。若自定义类型与 string 不能直接统一,应先显式转换成目标类型。
cmp.Or 会返回候选值的副本吗?
它按 Go 的普通值传递规则返回该值。对指针、通道或接口等引用语义相关的值,返回结果仍然指向相应对象,并不会自动深拷贝。
可以用 cmp.Or 代替所有 if 默认值判断吗?
不可以。需要区分零值与未设置、需要惰性计算、需要处理错误,或者候选类型不可比较时,显式 if 通常更清楚。
qooapp是游戏平台还是资讯社区?游戏商店、媒体与玩家功能边界说明
- 上一篇
- qooapp是游戏平台还是资讯社区?游戏商店、媒体与玩家功能边界说明
- 下一篇
- 繁花动漫免费吗?公开页面宣传、广告与内容来源边界说明
-
- Golang · Go教程 | 43分钟前 | go · Go reset Multistream gzip.Reader io.ByteReader
- Go gzip.Reader.Multistream 怎么处理拼接的 gzip 数据
- 301浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go bytes.Buffer.ReadFrom 怎么高效接收数据流
- 194浏览 收藏
-
- Golang · Go教程 | 1小时前 | go · Go io.Writer bytes.Reader WriteTo
- Go bytes.Reader.WriteTo 怎么把内存数据流式复制出去
- 466浏览 收藏
-
- Golang · Go教程 | 2小时前 | 文件处理 · go · Go bufio Scanner.Buffer 超长文本行
- Go bufio.Scanner.Buffer 怎么读取超长文本行
- 120浏览 收藏
-
- Golang · Go教程 | 2小时前 | 文件处理 · go · Go archive/tar next tar.Reader
- Go archive/tar.Reader.Next 怎么安全遍历归档条目
- 337浏览 收藏
-
- Golang · Go教程 | 3小时前 | 文件处理 · 标准库 · 资源管理 · go语言 · zip Go archive/zip OpenReader Reader.Open
- Go archive/zip.OpenReader 怎么按名称读取压缩包文件
- 130浏览 收藏
-
- Golang · Go教程 | 4小时前 |
- Go tls.ConnectionState 读取协商协议与证书信息
- 327浏览 收藏
-
- Golang · Go教程 | 4小时前 | go · 随机数 Go crypto/rand 令牌
- Go crypto/rand 生成令牌并避免短随机数
- 145浏览 收藏
-
- Golang · Go教程 | 5小时前 | go · 目录 Go 符号链接 archive/tar Header.Typeflag
- Go archive/tar Header.Typeflag 区分目录与链接
- 199浏览 收藏
-
- Golang · Go教程 | 5小时前 | go · Go archive/tar tar.Writer FileInfoHeader
- Go archive/tar 流式写入文件元数据
- 366浏览 收藏
-
- Golang · Go教程 | 5小时前 | go ·
- Go compress/zstd 多帧解压的内存控制
- 269浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 323次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 378次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 375次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 338次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 164次使用
-
- go语言标准库fmt包的一键入门
- 2023-01-24 335浏览
-
- Go泛型实战教程之如何在结构体中使用泛型
- 2023-02-24 234浏览
-
- 关于go-zero单体服务使用泛型简化注册Handler路由的问题
- 2022-12-30 346浏览
-
- Go1.18新特性工作区模糊测试及泛型的使用详解
- 2022-12-30 131浏览
-
- Go泛型应用工厂方法及泛型使用
- 2022-12-27 185浏览

