Go函数必须显式声明返回类型,不支持推导;参数均为值传递,修改原值需传指针;多返回值用括号包裹;命名返回值易致隐式return和零值bug,应慎用。

函数定义必须显式声明返回类型
Go 不支持类型推导的函数返回值,func 声明里返回类型不能省略,哪怕返回 error 或空结构体也要写清楚。漏写会导致编译错误 missing function body 或 syntax error: unexpected semicolon。
常见写法示例:
func add(a, b int) int {
return a + b
}
func split(n int) (int, int) {
return n / 2, n % 2
}
func doSomething() (string, error) {
return "done", nil
}
- 多返回值必须用括号包裹,如
(int, int),不能写成int, int - 命名返回值(如
func foo() (x, y int))会让变量在函数体内自动声明,但容易掩盖未赋值 bug,慎用 - 如果函数不返回任何值,括号仍需保留:必须写
func noop() {},不能写func noop {}
参数传递是值拷贝,指针用于修改原值
Go 所有参数都是值传递——包括 slice、map、chan 和 interface{}。它们底层含指针字段,所以“看起来”能修改底层数组或哈希表,但变量本身(如 slice header)仍是拷贝。
真正需要修改调用方变量内容时,得传指针:
立即学习“go语言免费学习笔记(深入)”;
func increment(x *int) {
*x++
}
n := 42
increment(&n) // n 变成 43
-
slice追加元素(append)可能触发底层数组扩容,此时原slice不受影响,除非你显式接收返回值:s = append(s, v) - 传
*struct{}比传大 struct 更高效,也更符合修改意图 - 不要对字符串、数组字面量取地址(如
&[3]int{1,2,3}),会报错cannot take the address of ...
返回多个值时,命名返回值易引发隐式 return 行为
命名返回值会让 Go 在函数末尾自动插入 return(即 “naked return”),这看似简洁,实则隐藏控制流,尤其在有 if/else 分支时容易出错。
func badDiv(a, b float64) (result float64, err error) {
if b == 0 {
err = fmt.Errorf("division by zero")
return // 这里 result 是 0(零值),不是你想返回的值
}
result = a / b
return // 隐式返回 result, err
}
- 上面函数中,
if分支提前return后,result保持零值,调用方可能误以为计算成功 - 建议只在简单函数(如 getter 或 error-only 判断)中用命名返回值;复杂逻辑一律显式写全
return val, err - 命名返回值名会进入文档(
godoc),可提升可读性,但代价是语义模糊风险
函数是一等公民,但闭包捕获的是变量引用而非快照
Go 函数可赋值给变量、作为参数传入、从函数返回,但闭包中引用的外部变量是“活”的——所有闭包共享同一份变量内存地址。
典型陷阱:
for i := 0; i < 3; i++ {
defer func() {
fmt.Println(i) // 全部输出 3
}()
}
- 解决办法是立即传参绑定当前值:
defer func(v int) { fmt.Println(v) }(i) - 或在循环内用新变量重声明:
for i := 0; i - 闭包内修改外部变量会影响后续调用,比如缓存计数器、状态机等场景需格外注意生命周期










