go中无函数指针,函数名即一等公民;可直接赋值传递,不可取地址;支持回调与高阶函数,依赖闭包捕获变量,签名须严格匹配。

Go里没有“函数指针”这个概念,只有函数值
Go语言不支持C-style的函数指针(比如 func(int) int*),所有函数名本身就是可赋值、可传递的一等公民。你拿到的不是地址,而是函数值本身——它底层可能包含代码指针+闭包环境,但对用户完全透明。
常见误解是试图用 &myFunc 取地址,这会报错:cannot take the address of myFunc。Go不允许取函数地址,也不需要。
- 直接用函数名赋值:
var f func(int) int = add - 函数类型声明必须完整(参数+返回值):
type Handler func(string) error - 匿名函数可直接赋值:
f := func(x int) int { return x * 2 }
怎么实现回调:把函数当参数传进去
回调的本质就是把函数作为参数传给另一个函数。Go里写法干净直接,没有额外语法糖或关键字。
典型场景:注册事件处理、异步完成通知、模板渲染钩子。
func DoWork(onSuccess func(int), onError func(error)) {
result := heavyCalc()
if result > 0 {
onSuccess(result)
} else {
onError(fmt.Errorf("invalid result: %d", result))
}
}
<p>// 调用时传入具体函数
DoWork(
func(n int) { fmt.Printf("success: %d\n", n) },
func(err error) { log.Println("fail:", err) },
)</p>
- 注意函数签名必须严格匹配,包括参数个数、类型、顺序和返回值
- 如果回调要捕获外部变量,直接用闭包即可,无需额外绑定
- 避免在回调里做耗时操作,除非明确设计为异步执行
高阶函数怎么写:返回函数的函数
高阶函数即接受函数为参数,或返回函数的函数。Go里返回函数很常见,比如中间件、装饰器、配置化构造器。
关键点:返回的函数类型必须显式声明,且生命周期由闭包变量决定。
func WithTimeout(timeout time.Duration) func(func()) {
return func(f func()) {
done := make(chan bool, 1)
go func() {
f()
done timeoutHandler := WithTimeout(2 <em> time.Second)
timeoutHandler(func() { time.Sleep(3 </em> time.Second) })
- 返回函数不能省略类型声明,否则编译失败
- 闭包捕获的变量(如
timeout)会在返回函数被调用时仍有效 - 别在高阶函数里启动 goroutine 后忘记回收资源(比如未关闭的 channel)
容易踩的坑:nil 函数调用 panic 和类型擦除
函数值可以为 nil,但直接调用会 panic:panic: call of nil function。这比 C 的空指针更早暴露问题,但也容易忽略检查。
另一个隐形问题是接口类型擦除:把函数赋给 interface{} 后,再想转回原函数类型,必须用类型断言,且断言失败会 panic。
- 安全调用前加判空:
if f != nil { f() } - 避免无意义地把函数塞进
interface{},尤其不要混用不同签名的函数 - 测试时记得覆盖
nil回调路径,否则线上可能 crash - 函数类型不支持反射修改(
reflect.Value.Call可用,但性能差、易出错,一般没必要)
函数值在 Go 里足够轻量,但它的行为不像整数那样“直觉”。真正要注意的不是语法,而是谁拥有闭包变量、何时执行、是否并发安全——这些才是实际项目里最容易翻车的地方。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











