Go 函数
函数是 Go 程序的基本构建单元。Go 的函数功能丰富,支持多返回值、命名返回值、可变参数、defer 延迟执行、匿名函数和闭包等特性。本篇将全面讲解 Go 函数的各种用法。
函数定义与调用
Go 使用 func 关键字定义函数:
package main
import "fmt"
// 定义一个接受两个参数并返回一个值的函数
func add(a int, b int) int {
return a + b
}
// 参数类型相同时可以简写
func multiply(a, b int) int {
return a * b
}
func main() {
sum := add(3, 5)
product := multiply(4, 6)
fmt.Println("3 + 5 =", sum) // 8
fmt.Println("4 * 6 =", product) // 24
}多返回值
Go 函数可以返回多个值,这是 Go 语言的一大特色:
package main
import "fmt"
// 返回商和余数
func divide(a, b int) (int, int) {
return a / b, a % b
}
func main() {
quotient, remainder := divide(17, 5)
fmt.Printf("17 / 5 = %d 余 %d\n", quotient, remainder)
}错误处理惯例
Go 通常用最后一个返回值表示操作是否成功:
package main
import (
"errors"
"fmt"
)
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("除数不能为零")
}
return a / b, nil
}
func main() {
result, err := divide(10, 3)
if err != nil {
fmt.Println("错误:", err)
return
}
fmt.Printf("10 / 3 = %.2f\n", result)
// 除零的情况
_, err = divide(10, 0)
if err != nil {
fmt.Println("错误:", err)
}
}命名返回值
Go 允许为返回值命名,命名返回值在函数开始时被初始化为零值,可以直接使用 return 返回:
package main
import "fmt"
func divide(a, b int) (quotient int, remainder int) {
quotient = a / b
remainder = a % b
return // 裸 return,自动返回命名返回值
}
func main() {
q, r := divide(17, 5)
fmt.Printf("商: %d, 余数: %d\n", q, r)
}建议:命名返回值适合用于简短函数,可以增强可读性。复杂函数中过度使用命名返回值反而会让代码难以理解。
可变参数
使用 ...Type 语法定义可变参数函数,参数在函数内部表现为切片:
package main
import "fmt"
// sum 接受任意数量的 int 参数
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
func main() {
fmt.Println(sum()) // 0
fmt.Println(sum(1)) // 1
fmt.Println(sum(1, 2, 3)) // 6
fmt.Println(sum(1, 2, 3, 4, 5)) // 15
}传递切片给可变参数
使用 ... 展开切片:
package main
import "fmt"
func printNums(nums ...int) {
for i, n := range nums {
fmt.Printf("[%d] = %d\n", i, n)
}
}
func main() {
values := []int{10, 20, 30, 40}
printNums(values...) // 展开切片
}defer 语句
defer 用于延迟函数的执行,直到包含它的函数返回。多个 defer 按照**后进先出(LIFO)**的顺序执行。
基本用法
package main
import "fmt"
func main() {
fmt.Println("开始")
defer fmt.Println("defer 1")
defer fmt.Println("defer 2")
defer fmt.Println("defer 3")
fmt.Println("结束")
}
// 输出顺序:
// 开始
// 结束
// defer 3
// defer 2
// defer 1defer 的典型场景
package main
import (
"fmt"
"os"
)
func readFile(filename string) {
// 打开文件
f, err := os.Open(filename)
if err != nil {
fmt.Println("打开文件失败:", err)
return
}
// 确保函数结束时关闭文件
defer f.Close()
// 读取文件内容...
fmt.Println("文件已打开,正在处理...")
}
func main() {
readFile("test.txt")
}defer 的参数在声明时就确定
package main
import "fmt"
func main() {
x := 10
defer fmt.Println("defer:", x) // x 的值在 defer 声明时就确定了
x = 20
fmt.Println("main:", x)
}
// 输出:
// main: 20
// defer: 10匿名函数
匿名函数是没有名字的函数,可以直接定义并调用:
package main
import "fmt"
func main() {
// 定义并立即调用
result := func(a, b int) int {
return a + b
}(3, 5)
fmt.Println("结果:", result) // 8
// 赋值给变量
greet := func(name string) {
fmt.Println("Hello,", name)
}
greet("Go")
}闭包
闭包是函数与其引用的外部变量的组合。闭包可以"记住"外部变量的状态。
计数器示例
package main
import "fmt"
// newCounter 返回一个闭包函数
func newCounter() func() int {
count := 0 // 闭包捕获的变量
return func() int {
count++
return count
}
}
func main() {
counter := newCounter()
fmt.Println(counter()) // 1
fmt.Println(counter()) // 2
fmt.Println(counter()) // 3
// 每个闭包实例有独立的 count
counter2 := newCounter()
fmt.Println(counter2()) // 1
fmt.Println(counter2()) // 2
}闭包的实际应用
package main
import (
"fmt"
"strings"
)
// 生成带前缀的日志函数
func makeLogger(prefix string) func(string) {
return func(msg string) {
fmt.Printf("[%s] %s\n", prefix, strings.ToUpper(msg))
}
}
func main() {
infoLog := makeLogger("INFO")
errorLog := makeLogger("ERROR")
infoLog("程序启动") // [INFO] 程序启动
errorLog("连接失败") // [ERROR] 连接失败
infoLog("处理完成") // [INFO] 处理完成
}panic 和 recover 基础
Go 使用 panic 和 recover 来处理程序中的严重错误(类似其他语言的异常机制)。
panic
panic 会中断正常的控制流,开始执行 defer 函数链:
package main
import "fmt"
func main() {
fmt.Println("程序开始")
// 模拟一个不可恢复的错误
panic("发生了严重错误!")
fmt.Println("这行不会执行")
}recover
recover 只能在 defer 函数中使用,用于捕获 panic:
package main
import "fmt"
func safeDivide(a, b int) (result int, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("发生 panic: %v", r)
}
}()
if b == 0 {
panic("除数为零")
}
result = a / b
return
}
func main() {
r1, e1 := safeDivide(10, 2)
fmt.Printf("10/2 = %d, err = %v\n", r1, e1)
r2, e2 := safeDivide(10, 0)
fmt.Printf("10/0 = %d, err = %v\n", r2, e2)
}注意:
panic和recover应该谨慎使用。对于可预期的错误,推荐使用error返回值。panic适用于真正的"不可能发生"的情况。
总结
本篇全面介绍了 Go 函数的各种特性。Go 函数支持多返回值,配合 error 类型实现优雅的错误处理;命名返回值增强了代码可读性;可变参数让函数更加灵活;defer 语句确保了资源的可靠释放;匿名函数和闭包为函数式编程提供了支持,计数器模式是闭包的经典应用。最后,panic 和 recover 提供了处理严重错误的机制,但日常开发中应优先使用 error 返回值。