Go中匿名函数声明为func(){}(),需加括号调用;闭包捕获变量引用,循环中易因共享变量导致所有goroutine输出同一值。

Go 里匿名函数怎么声明和调用
Go 的匿名函数就是没有名字的函数字面量,直接写出来就能用,不靠 func 声明再赋值。它必须带括号调用,否则只是个函数值,不会执行。
常见错误是写完匿名函数忘了加 (),结果程序没报错但逻辑没触发——比如在 go 关键字后漏掉括号,协程根本没启动。
- 基本写法:
func() { fmt.Println("hello") }() - 带参数和返回值:
func(x int) int { return x * 2 }(5) - 赋值给变量后再调用:
f := func() { println("ok") }; f()
闭包捕获变量时要注意什么
Go 的闭包会捕获外部变量的引用(不是快照),循环中创建多个匿名函数却共用同一个变量,是高频翻车点。
典型现象:for 循环里起 goroutine 调用匿名函数,所有 goroutine 最后都打印最后一个迭代值。
立即学习“go语言免费学习笔记(深入)”;
- 错例:
for i := 0; i → 输出可能是 <code>333 - 正解一(传参):
for i := 0; i - 正解二(局部副本):
for i := 0; i
注意:第二种写法依赖 Go 1.22+ 对循环变量作用域的改进;老版本仍推荐显式传参。
匿名函数作为参数传给标准库函数
很多 Go 标准库函数接受 func() 类型参数,比如 sync.Once.Do、http.HandleFunc、sort.SliceStable 的比较函数。
关键不是“能不能写”,而是“参数签名必须严格匹配”。比如 http.HandleFunc 要求的是 func(http.ResponseWriter, *http.Request),少一个参数或类型不对都会编译失败。
http.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("ok")) })sort.SliceStable(items, func(i, j int) bool { return items[i].Name- 错误示例:
sort.SliceStable(items, func(i, j int) int { ... })→ 返回类型应为bool,不是int
性能和逃逸:匿名函数会让变量逃逸到堆上吗
会,而且很常见。只要匿名函数捕获了栈上的变量(哪怕只是读取),编译器通常会把该变量分配到堆上,避免函数返回后访问悬空指针。
这不是 bug,是 Go 编译器的保守决策。但如果你在热点路径频繁创建闭包,又捕获了大结构体,就可能引发额外 GC 压力。
- 用
go build -gcflags="-m" main.go查看逃逸分析结果,留意move to heap提示 - 简单场景(如只捕获 int、string)影响极小,不用过度优化
- 高并发服务中,避免在循环内反复构造捕获大量字段的闭包
闭包真正的复杂点不在语法,而在变量生命周期和 goroutine 交互的时机——那个 i := i 看似多余,其实是在跟编译器和调度器打配合。


















