4.4 KiB
4.4 KiB
tags, create time
| tags | create time | ||||
|---|---|---|---|---|---|
|
2026-06-07 15:00 |
Go 语言函数
概述
Go 的函数是一等公民:可以赋值给变量、作为参数传递、返回匿名函数(闭包)。本文覆盖多返回值、变参、闭包、匿名函数和值传递机制。
正文
为什么 Go 函数支持多返回值?
在多数语言中,函数只能返回一个值——错误信息需要单独处理(如异常或输出参数)。Go 的选择是:让错误成为显式的返回值。
name, age, err := GetUser(42)
if err != nil {
return err
}
这比隐藏的错误处理更透明——编译器强制你处理每一个返回值。
基本定义与调用
func swap(x, y string) (string, string) {
return y, x
}
a, b := swap("Google", "Hello")
fmt.Println(a, b) // Hello Google
无返回值时省略返回类型;有返回值时必须写:
func max(a, b int) int {
if a > b {
return a
}
return b
}
func greet(name string) { // 无返回值
fmt.Printf("Hello, %s!\n", name)
}
命名返回值
Go 允许给返回值命名,return 可以不写具体值(直接返回):
func Split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return // 空白 return,返回当前 x, y 的值
}
[!tip] 💡 命名返回值的用途 虽然不常用,但在复杂函数的"提前返回"场景中可以让代码更清晰——读者一眼就知道返回了什么。
变参
变参函数接受零个或多个同类型参数:
func Sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
Sum(1, 2, 3) // 6
Sum(1, 2, 3, 4, 5) // 15
// 将切片展开传入
nums := []int{1, 2, 3, 4}
Sum(nums...) // 10
[!warning] ⚠️
...int底层是[]int在函数内部,nums就是一个 slice。用range nums遍历时要注意前面提到的闭包陷阱。
函数是一等公民
函数可以赋值给变量、作为参数传递:
// 函数变量
getSquareRoot := func(x float64) float64 {
return math.Sqrt(x)
}
fmt.Println(getSquareRoot(9)) // 3
// 函数作为参数(回调)
type Calculator func(int, int) int
func apply(a, b int, op Calculator) int {
return op(a, b)
}
result := apply(3, 4, func(x, y int) int {
return x + y
}) // 7
匿名函数与闭包
匿名函数是没有名称的函数,通常内联使用:
func() {
fmt.Println("我是匿名函数")
}() // 末尾 () 立即执行
闭包 = 匿名函数 + 捕获的外部变量。闭包会"记住"它创建时的环境变量:
func counter() func() int {
i := 0
return func() int {
i++
return i
}
}
next := counter()
fmt.Println(next()) // 1
fmt.Println(next()) // 2
fmt.Println(next()) // 3
next2 := counter() // 全新的计数器
fmt.Println(next2()) // 1 — 独立于 next
[!note] 📝 每次调用 counter() 都创建新的闭包环境
next和next2各自维护独立的i变量,互不影响。这是闭包的核心特性。
graph LR
A["counter()"] --> B["i = 0"]
B --> C["返回闭包 → next"]
C --> D["next() 调用: i→1"]
D --> E["next() 调用: i→2"]
A --> F["counter() 再次调用"]
F --> G["i = 0 新变量"]
G --> H["返回闭包 → next2"]
H --> I["next2() 调用: i→1"]
[!warning] ⚠️ 闭包捕获的是引用 如果外部变量在闭包外被修改,闭包看到的也是修改后的值。配合
for range使用时要特别注意(见「循环」章节)。
值传递:Go 只有值传递
Go 的所有参数传递都是值传递——参数被拷贝一份传给函数。但对于 slice、map、chan 这些引用类型,拷贝的是内部的指针,所以修改内容会影响原数据。
func addOne(x int) {
x++ // 只修改了副本
}
func main() {
a := 5
addOne(a)
fmt.Println(a) // 5 — 没变!
}
如果需要修改原值,传指针:
func addOnePtr(x *int) {
*x++
}
a := 5
addOnePtr(&a)
fmt.Println(a) // 6 ✅
关键要点总结
| 特性 | 说明 |
|---|---|
| 多返回值 | Go 独有,广泛用于错误处理 |
变参 ...T |
底层是 []T,可用 ... 展开 slice |
| 闭包 | 匿名函数捕获外部变量,形成独立状态 |
| 值传递 | 所有参数都是值拷贝,引用类型的"引用"本质是指针拷贝 |