函数

基本声明

Go 函数声明遵循 func 函数名(参数列表) 返回值 的格式,类型位于名称之后:

package main

import "fmt"

// 单返回值
func add(a, b int) int {
    return a + b
}

func main() {
    fmt.Println(add(1, 2)) // 3
}

相同类型的相邻参数可合并声明类型,如 a, b int

多返回值

多返回值是 Go 的核心特性,最典型的用途是同时返回结果与错误:

package main

import (
    "errors"
    "fmt"
)

func divide(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("division by zero")
    }
    return a / b, nil
}

func main() {
    result, err := divide(10, 2)
    if err != nil {
        fmt.Println("错误:", err)
        return
    }
    fmt.Println("结果:", result)
}

若只关心其中一个返回值,用空标识符 _ 接收被忽略的部分。

命名返回值

返回值可以命名,命名后的返回值相当于函数内的局部变量;裸 return 会返回其当前值:

func split(sum int) (x, y int) {
    x = sum * 4 / 9
    y = sum - x
    return // 裸返回:返回 x, y 的当前值
}
Tip

命名返回值适用于简短函数或需要文档化语义的场景(如 (n int, err error))。在复杂函数中滥用裸返回会降低可读性,建议显式 return

可变参数

可变参数在函数内部表现为切片:

package main

import "fmt"

func sum(nums ...int) int {
    total := 0
    for _, n := range nums {
        total += n
    }
    return total
}

func main() {
    fmt.Println(sum(1, 2, 3)) // 6

    // 展开切片传入
    nums := []int{4, 5, 6}
    fmt.Println(sum(nums...)) // 15
}

函数是一等公民

函数可以作为参数、返回值与变量,这是实现回调、中间件等模式的基础:

package main

import (
    "fmt"
    "strings"
)

// 函数作为参数
func apply(s string, fn func(string) string) string {
    return fn(s)
}

// 函数作为返回值(闭包)
func multiplier(factor int) func(int) int {
    return func(n int) int {
        return n * factor
    }
}

func main() {
    fmt.Println(apply("go", strings.ToUpper)) // GO

    double := multiplier(2)
    fmt.Println(double(21)) // 42
}

defer:延迟执行

defer 将函数调用推迟到当前函数返回之前执行,多个 defer后进先出顺序执行,常用于资源释放:

package main

import (
    "fmt"
    "os"
)

func readFile(name string) error {
    f, err := os.Open(name)
    if err != nil {
        return err
    }
    defer f.Close() // 确保函数任何退出路径都会关闭文件

    buf := make([]byte, 128)
    _, err = f.Read(buf)
    return err
}

func main() {
    // 输出顺序:3 2 1(栈式执行)
    for i := 1; i <= 3; i++ {
        defer fmt.Println(i)
    }
    fmt.Println("main 返回前依次执行 defer")
}
Tip

defer 的典型场景包括:关闭文件、解锁互斥锁、recover 恢复 panic。求值时机的规则是:defer 语句执行时即完成参数求值,但调用推迟到返回前。

需要控制互斥锁时,也常用 defer mu.Unlock() 避免提前 return 漏解锁。

小结

  • 多返回值配合 error 构成 Go 的错误处理惯例。
  • 函数与闭包支撑回调、中间件等工程模式。
  • defer 遵循后进先出,用于保障资源释放。