go中无语言级纯函数标记,但可通过约束实现:输入相同则输出必相同、不读全局/闭包变量、不修改传入数据;违反任一条件即不纯,影响并发安全与可测试性。

Go里没有语言级的“纯函数”语法标记,但你可以靠约束写法让它真正纯——关键不是“能不能”,而是“敢不敢不碰外部状态”。
纯函数必须满足的三个硬性条件
判断一个 Go 函数是否纯,只看它在调用时是否同时满足以下三点:
- 输入相同,输出必然相同(
add(2, 3)永远返回5) - 不读取任何全局变量、包级变量或闭包捕获的可变变量(
counter、config.URL都算违规) - 不修改任何传入的指针、切片底层数组、map 或结构体字段(哪怕只是
user.Name = "x"也算破功)
只要其中任意一条被打破,它就不是纯函数。编译器不会报错,但并发安全、可测试性、缓存能力全会打折。
值传递 vs 指针传递:纯函数里的参数陷阱
很多人以为“传指针=不纯”,其实不然——纯函数可以接收指针,只要你不解引用修改它。
常见错误是把 *User 当作“只读输入”,结果在函数里写了 u.Name = "new"。这直接触发副作用。
正确做法是:
- 若只需读取,用值传递(
func NameLen(u User) int),避免意外修改风险 - 若结构体太大(如含
[1024]byte字段),用func NameLen(u *User) int,但函数体内只读u.Name,绝不赋值 - 若必须返回新状态,显式构造并返回新值:
func WithName(u User, name string) User { u.Name = name; return u }
注意:strings.Split 是纯的,因为它只读输入字符串,返回新切片;而 sort.Strings 不纯,它原地修改切片底层数组。
闭包与纯函数:小心隐式依赖
闭包天然容易引入外部状态,一不小心就破坏纯度。
这个函数看着像纯函数,实则不是:
func MakeAdder(base int) func(int) int {
return func(x int) int {
return base + x // ✅ 读取闭包变量 OK
}
}
adder := MakeAdder(10)
fmt.Println(adder(5)) // 15
它仍是纯的——因为 base 在闭包创建时已固定,不可变。但如果改成这样:
func MakeCounter() func() int {
count := 0
return func() int {
count++ // ❌ 修改闭包变量 → 副作用 → 不纯
return count
}
}
每次调用返回不同结果,且影响后续调用,彻底违背纯函数定义。
无返回值函数 ≠ 有副作用,但通常就是
Go 中省略返回类型(如 func Log(msg string))本身不表示纯或不纯,它只说明“不返回计算结果”。但这类函数绝大多数用于打日志、发 HTTP 请求、写文件——全是副作用。
如果你真想写一个“无返回值但纯”的函数,它只能做两件事:
- 空操作(
func Noop()) - 或触发 panic(但 panic 不是正常控制流,也不算纯)
所以实践中,**所有无返回值函数都应默认视为有副作用**,并在函数名中体现意图,比如 LogError、SaveToDB、SendEmail。别用 Process 或 Handle 这类模糊动词掩盖副作用。
纯函数几乎总是带返回值的——否则它什么也没“产出”,调用它就没有意义。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











