Golang中匿名函数的巧妙用法:不仅限于并发场景
Golang中匿名函数的巧妙用法:不仅限于并发场景 我突然想到,我几乎只将匿名函数用作编写并发和并行代码的简便方式,极少情况下会从另一个函数返回一个函数。在 Go 中,还有哪些场景适合使用匿名函数?
6 回复
也许可以作为一个回调函数:
package main
import (
"fmt"
"time"
)
type callback func()
func main() {
doSth(func() {
fmt.Print("Jobs done")
})
}
func doSth(c callback) {
time.Sleep(time.Second * 2) // 模拟一些任务
c()
}
我最常在 sort.Slice 中使用匿名函数作为 less 参数。sort package - sort - Go Packages
func main() {
fmt.Println("hello world")
}
你好 @ashinnv,
匿名函数存在于定义它的函数的上下文中。(这就是为什么匿名函数也被称为“闭包”。)
闭包绑定到其外部作用域。也就是说,闭包可以访问定义该闭包的函数中定义的变量。这样,你就可以创建携带某种状态的函数,例如一个计数器,如 Go by Example: Closures 所示。
Jon Calhoun 描述了 Go 中闭包的 5 种有用用法。
我也将它们用作 C# 局部函数 的等价物。我能想到的一个例子是,如果我需要为类型构建一些元数据,例如:
type typeData struct {
// 一些我希望为每个类型保存的数据,可能是该类型的字段
// 到获取或设置值的函数等的映射。
}
var typeDataByReflectType = sync.Map{}
func getTypeData(rt reflect.Type) *typeData {
// newTypeData 是 getTypeData 的"局部"函数,因为你不应该
// 能够在 getTypeData 之外调用它,即使是在同一个包中的其他函数里。
newTypeData := func(rt reflect.Type) (td *typeData) {
// 执行创建这些 *typeData 结构体所需的任何操作
return
}
v, loaded := typeDataByReflectType.Load(rt)
if loaded {
return v.(*typeData)
}
td := newTypeData(rt)
if v, loaded = typeDataByReflectType.LoadOrStore(rt, td); loaded {
return v.(*typeData)
}
return td
}
在Go中,匿名函数确实远不止并发场景。以下是一些典型用例:
1. 延迟执行与资源管理
func processFile(filename string) error {
f, err := os.Open(filename)
if err != nil {
return err
}
defer func() {
if err := f.Close(); err != nil {
log.Printf("关闭文件失败: %v", err)
}
}()
// 文件处理逻辑
return nil
}
2. 实现闭包捕获状态
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
func main() {
c := counter()
fmt.Println(c()) // 1
fmt.Println(c()) // 2
fmt.Println(c()) // 3
}
3. 回调函数与事件处理
type EventHandler func(string)
func registerEvent(name string, handler EventHandler) {
// 注册逻辑
handler("事件触发: " + name)
}
func main() {
registerEvent("click", func(msg string) {
fmt.Println("处理点击:", msg)
})
}
4. 排序与比较逻辑
type Person struct {
Name string
Age int
}
func main() {
people := []Person{
{"Alice", 30},
{"Bob", 25},
{"Charlie", 35},
}
sort.Slice(people, func(i, j int) bool {
return people[i].Age < people[j].Age
})
fmt.Println(people) // [{Bob 25} {Alice 30} {Charlie 35}]
}
5. 中间件与装饰器模式
func loggingMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next(w, r)
log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
}
}
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello")
}
func main() {
http.HandleFunc("/", loggingMiddleware(handler))
http.ListenAndServe(":8080", nil)
}
6. 错误处理包装
func withRetry(fn func() error, maxRetries int) error {
var err error
for i := 0; i < maxRetries; i++ {
err = fn()
if err == nil {
return nil
}
time.Sleep(time.Second * time.Duration(i+1))
}
return fmt.Errorf("重试失败: %v", err)
}
func main() {
err := withRetry(func() error {
// 可能失败的操作
return nil
}, 3)
}
7. 配置选项模式
type Server struct {
addr string
timeout time.Duration
}
type Option func(*Server)
func WithTimeout(d time.Duration) Option {
return func(s *Server) {
s.timeout = d
}
}
func NewServer(addr string, opts ...Option) *Server {
s := &Server{addr: addr, timeout: 30 * time.Second}
for _, opt := range opts {
opt(s)
}
return s
}
func main() {
srv := NewServer(":8080", WithTimeout(60*time.Second))
}
8. 测试中的模拟函数
func TestProcess(t *testing.T) {
mockDB := func(query string) ([]string, error) {
return []string{"result1", "result2"}, nil
}
results, err := processWithDB(mockDB, "SELECT * FROM users")
// 断言逻辑
}
匿名函数在Go中的核心价值在于其能够捕获上下文、延迟执行、以及作为一等公民的函数式编程能力。这些特性使其在资源管理、配置、测试和中间件等场景中极为实用。

