Go 函数

函数定义

基本语法

package main
 
import "fmt"
 
// 基本函数定义
func add(a int, b int) int {
    return a + b
}
 
// 参数类型相同可以简写
func subtract(a, b int) int {
    return a - b
}
 
// 多个返回值
func divide(a, b int) (int, error) {
    if b == 0 {
        return 0, fmt.Errorf("除数不能为 0")
    }
    return a / b, nil
}
 
// 命名返回值
func multiply(a, b int) (result int) {
    result = a * b  // 直接使用命名返回值
    return          // 自动返回 result
}
 
// 多个命名返回值
func calculate(a, b int) (sum, product int) {
    sum = a + b
    product = a * b
    return  // 自动返回 sum 和 product
}
 
func main() {
    // 调用函数
    result := add(3, 5)
    fmt.Println(result)  // 8
    
    // 多个返回值
    quotient, err := divide(10, 2)
    if err != nil {
        fmt.Printf("错误: %v\n", err)
    } else {
        fmt.Printf("结果: %d\n", quotient)  // 5
    }
    
    // 忽略返回值
    _, err2 := divide(10, 0)
    if err2 != nil {
        fmt.Printf("错误: %v\n", err2)
    }
    
    // 命名返回值
    sum, product := calculate(3, 4)
    fmt.Printf("和: %d, 积: %d\n", sum, product)  // 和: 7, 积: 12
}

可变参数

package main
 
import "fmt"
 
// 可变参数函数
func sum(numbers ...int) int {
    total := 0
    for _, num := range numbers {
        total += num
    }
    return total
}
 
// 可变参数必须在最后
func printInfo(name string, numbers ...int) {
    fmt.Printf("姓名: %s, 数字: %v\n", name, numbers)
}
 
// 展开切片作为可变参数
func main() {
    // 调用可变参数函数
    result1 := sum(1, 2, 3, 4, 5)
    fmt.Println(result1)  // 15
    
    result2 := sum(10, 20)
    fmt.Println(result2)  // 30
    
    result3 := sum()  // 可以传入 0 个参数
    fmt.Println(result3)  // 0
    
    // 展开切片
    numbers := []int{1, 2, 3, 4, 5}
    result4 := sum(numbers...)  // 使用 ... 展开切片
    fmt.Println(result4)  // 15
    
    // 混合参数
    printInfo("Alice", 1, 2, 3)
}

函数作为值

package main
 
import "fmt"
 
// 函数可以作为值传递
func add(a, b int) int {
    return a + b
}
 
func multiply(a, b int) int {
    return a * b
}
 
// 函数类型
type Operation func(int, int) int
 
// 函数作为参数
func calculate(a, b int, op Operation) int {
    return op(a, b)
}
 
// 函数作为返回值
func getOperation(opType string) Operation {
    switch opType {
    case "add":
        return add
    case "multiply":
        return multiply
    default:
        return func(a, b int) int { return 0 }  // 匿名函数
    }
}
 
func main() {
    // 函数作为值
    var op Operation = add
    result := op(3, 5)
    fmt.Println(result)  // 8
    
    // 函数作为参数
    result1 := calculate(3, 5, add)
    result2 := calculate(3, 5, multiply)
    fmt.Printf("add: %d, multiply: %d\n", result1, result2)
    
    // 函数作为返回值
    addOp := getOperation("add")
    result3 := addOp(10, 20)
    fmt.Println(result3)  // 30
}

匿名函数和闭包

package main
 
import "fmt"
 
func main() {
    // 匿名函数
    func() {
        fmt.Println("匿名函数")
    }()  // 立即执行
    
    // 匿名函数赋值给变量
    greet := func(name string) {
        fmt.Printf("Hello, %s!\n", name)
    }
    greet("Alice")
    
    // 闭包:函数可以访问外部变量
    counter := 0
    increment := func() {
        counter++  // 访问外部变量
        fmt.Printf("计数: %d\n", counter)
    }
    
    increment()  // 计数: 1
    increment()  // 计数: 2
    increment()  // 计数: 3
    
    // 闭包示例:生成器
    generator := createGenerator()
    fmt.Println(generator())  // 1
    fmt.Println(generator())  // 2
    fmt.Println(generator())  // 3
    
    // 闭包陷阱:循环变量
    var funcs []func()
    for i := 0; i < 3; i++ {
        // 错误写法:所有函数都引用同一个 i
        // funcs = append(funcs, func() {
        //     fmt.Println(i)  // 都会打印 3
        // })
        
        // 正确写法:创建局部变量
        i := i  // 创建新的局部变量
        funcs = append(funcs, func() {
            fmt.Println(i)  // 打印 0, 1, 2
        })
    }
    
    for _, f := range funcs {
        f()
    }
}
 
// 生成器函数
func createGenerator() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}

defer 和函数

package main
 
import "fmt"
 
func main() {
    // defer 在函数返回前执行
    deferExample()
    
    // defer 可以修改命名返回值
    result := deferReturn()
    fmt.Println(result)  // 1(不是 0)
    
    // defer 与 panic/recover
    deferPanic()
}
 
func deferExample() {
    fmt.Println("开始")
    defer fmt.Println("延迟 1")
    defer fmt.Println("延迟 2")
    fmt.Println("结束")
    // 输出:
    // 开始
    // 结束
    // 延迟 2
    // 延迟 1
}
 
func deferReturn() (result int) {
    defer func() {
        result++  // 修改返回值
    }()
    return 0  // 实际返回 1
}
 
func deferPanic() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Printf("捕获到 panic: %v\n", r)
        }
    }()
    
    panic("发生错误")
    fmt.Println("这行不会执行")
}

方法(Method)

package main
 
import "fmt"
 
// 定义类型
type Rectangle struct {
    Width  float64
    Height float64
}
 
// 值接收者方法
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}
 
// 指针接收者方法(可以修改接收者)
func (r *Rectangle) Scale(factor float64) {
    r.Width *= factor
    r.Height *= factor
}
 
// 值接收者 vs 指针接收者
type Point struct {
    X, Y int
}
 
// 值接收者:不会修改原始值
func (p Point) MoveByValue(dx, dy int) {
    p.X += dx
    p.Y += dy
}
 
// 指针接收者:会修改原始值
func (p *Point) MoveByPointer(dx, dy int) {
    p.X += dx
    p.Y += dy
}
 
func main() {
    // 使用值接收者方法
    rect := Rectangle{Width: 10, Height: 5}
    area := rect.Area()
    fmt.Printf("面积: %.2f\n", area)  // 50.00
    
    // 使用指针接收者方法
    rect.Scale(2)
    fmt.Printf("缩放后: %.2f x %.2f\n", rect.Width, rect.Height)  // 20.00 x 10.00
    
    // 值接收者 vs 指针接收者
    point := Point{X: 1, Y: 2}
    point.MoveByValue(10, 10)
    fmt.Printf("值接收者: %+v\n", point)  // {X:1 Y:2}(未改变)
    
    point.MoveByPointer(10, 10)
    fmt.Printf("指针接收者: %+v\n", point)  // {X:11 Y:12}(已改变)
}

接口和函数

package main
 
import "fmt"
 
// 接口定义
type Writer interface {
    Write([]byte) (int, error)
}
 
// 函数类型实现接口
type WriterFunc func([]byte) (int, error)
 
func (f WriterFunc) Write(p []byte) (int, error) {
    return f(p)
}
 
// 使用示例
func main() {
    // 普通函数
    writeFunc := func(data []byte) (int, error) {
        fmt.Println(string(data))
        return len(data), nil
    }
    
    // 转换为接口
    var writer Writer = WriterFunc(writeFunc)
    writer.Write([]byte("Hello, World!"))
}

函数最佳实践

package main
 
import (
    "fmt"
    "io"
    "os"
)
 
// 1. 错误处理:总是返回错误
func readFile(filename string) ([]byte, error) {
    file, err := os.Open(filename)
    if err != nil {
        return nil, err  // 返回错误
    }
    defer file.Close()
    
    return io.ReadAll(file)
}
 
// 2. 函数应该简短,职责单一
// 不好的例子:一个函数做太多事情
func badFunction() {
    // 读取文件
    // 处理数据
    // 写入数据库
    // 发送邮件
    // ...
}
 
// 好的例子:拆分成多个函数
func goodFunction() {
    data := readData()
    processed := processData(data)
    saveData(processed)
    sendNotification()
}
 
func readData() []byte {
    // 读取数据
    return nil
}
 
func processData(data []byte) []byte {
    // 处理数据
    return nil
}
 
func saveData(data []byte) {
    // 保存数据
}
 
func sendNotification() {
    // 发送通知
}
 
// 3. 使用命名返回值提高可读性
func divide(a, b int) (quotient int, remainder int, err error) {
    if b == 0 {
        err = fmt.Errorf("除数不能为 0")
        return
    }
    quotient = a / b
    remainder = a % b
    return
}
 
// 4. 使用 defer 进行资源清理
func processFile(filename string) error {
    file, err := os.Open(filename)
    if err != nil {
        return err
    }
    defer file.Close()  // 确保文件被关闭
    
    // 处理文件
    return nil
}
 
// 5. 函数文档注释
// CalculateSum 计算两个整数的和
// 参数:
//   a: 第一个整数
//   b: 第二个整数
// 返回:
//   两个整数的和
func CalculateSum(a, b int) int {
    return a + b
}

总结

Go 函数的特点:

  1. 多返回值:可以返回多个值,常用于错误处理
  2. 命名返回值:提高代码可读性
  3. 可变参数:使用 ... 语法
  4. 函数作为值:函数是一等公民
  5. 闭包:函数可以访问外部变量
  6. 方法:可以为类型定义方法
  7. defer:延迟执行,用于资源清理

掌握这些函数特性后,就可以编写灵活、可复用的代码了!


go 函数 方法 闭包 defer