首页 / Go 语言入门教程 / 函数声明与调用

Go 语言入门教程

函数声明与调用

本教程共 80 篇 · 第 22 篇 · 更新于 2026-07-27 · 约 8 分钟阅读

GoGo 入门教程func函数多返回值命名返回值Go 函数

22. 函数声明与调用

本节目标:掌握 Go 函数的声明和调用,理解参数传递、多返回值、命名返回值的用法。

函数是一等公民

在 Go 里,函数是一等公民(First-class citizen)。这意味着函数可以:

  • 赋值给变量
  • 作为参数传递
  • 作为返回值返回
  • 存储在数据结构里

函数是 Go 程序最核心的组成部分。

函数声明

基本格式:

func 函数名(参数列表) 返回值 {
    函数体
}

来看一个完整的例子:

func add(a int, b int) int {
    return a + b
}
  • func 是声明函数的关键字
  • add 是函数名
  • a int, b int 是参数列表
  • int 是返回值类型
  • return a + b 是函数体

调用函数

result := add(3, 4)
fmt.Println(result)  // 7

参数

同类型参数简写

相邻的同类型参数可以只写一次类型:

func add(a, b int) int {
    return a + b
}

等价于 func add(a int, b int) int

参数是值传递

Go 的参数传递是值传递(Pass by value)。函数收到的是参数的副本,修改不影响原值:

func modify(x int) {
    x = 100
}

func main() {
    n := 10
    modify(n)
    fmt.Println(n)  // 10,没有被修改
}

如果你想在函数里修改外部变量,需要传指针:

func modify(x *int) {
    *x = 100
}

func main() {
    n := 10
    modify(&n)
    fmt.Println(n)  // 100
}
Note

切片、map、channel 虽然也是值传递,但它们底层包含指针,所以看起来像引用传递。修改切片或 map 的元素会影响原数据,但重新赋值不会。

返回值

无返回值

func greet(name string) {
    fmt.Println("Hello,", name)
}

不需要写返回值类型,也不需要 return(除非想提前返回)。

单个返回值

func square(x int) int {
    return x * x
}

多返回值

Go 支持多返回值,这是 Go 的特色。最常见的用法是返回值和错误:

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.Println(result)  // 3.3333...
}

多返回值用括号括起来,调用时用多个变量接收。

忽略某个返回值

_ 忽略不需要的返回值:

result, _ := divide(10, 3)  // 忽略错误(不推荐)
_, err := divide(10, 3)      // 只取错误
Warning

忽略错误是不好的习惯。在实际开发中,应该检查每个 error。

命名返回值

Go 允许给返回值起名字,叫做命名返回值

func sum(a, b int) (result int) {
    result = a + b
    return  // 裸 return,自动返回 result
}

命名返回值在函数开始时就被声明为零值。你可以在函数体里直接使用它,return 时不需要显式写出返回的变量。

多个命名返回值

func calculate(a, b int) (sum, product int) {
    sum = a + b
    product = a * b
    return
}

func main() {
    s, p := calculate(3, 4)
    fmt.Println(s, p)  // 7 12
}

命名返回值的文档作用

命名返回值可以作为文档,让调用者知道每个返回值的含义:

func getUser(id int) (name string, age int, err error) {
    // ...
}

调用者一看就知道第一个返回值是名字,第二个是年龄,第三个是错误。

return 覆盖命名返回值

如果 return 后面显式写了值,会覆盖命名返回值:

func compute(a, b int) (result int) {
    result = a + b
    return a * b  // 返回 a*b,不是 result
}
Tip

命名返回值适合短函数。函数太长时,裸 return 会让读者困惑返回的是什么值,建议显式写 return result

Go 不支持函数重载

Go 不支持同名函数重载:

func add(a, b int) int {
    return a + b
}

func add(a, b float64) float64 {  // 报错:add redeclared
    return a + b
}

Go 的设计理念是:如果签名不同,就是不同的函数,应该取不同的名字。

需要处理不同类型时,有两个选择:

  1. 起不同的名字:
func addInt(a, b int) int { return a + b }
func addFloat(a, b float64) float64 { return a + b }
  1. 用泛型(Go 1.18+):
func add[T int | float64](a, b T) T {
    return a + b
}

函数作为值

函数可以赋值给变量,也可以作为参数和返回值:

func main() {
    // 赋值给变量
    f := add
    fmt.Println(f(3, 4))  // 7

    // 作为参数
    result := apply(add, 3, 4)
    fmt.Println(result)  // 7
}

func add(a, b int) int {
    return a + b
}

func apply(f func(int, int) int, a, b int) int {
    return f(a, b)
}

函数类型写法:func(参数类型) 返回值类型。比如 func(int, int) int 表示接收两个 int 返回一个 int 的函数。

函数类型

可以定义自己的函数类型:

type MathFunc func(int, int) int

func apply(f MathFunc, a, b int) int {
    return f(a, b)
}

func main() {
    var add MathFunc = func(a, b int) int {
        return a + b
    }
    fmt.Println(apply(add, 3, 4))  // 7
}

init 函数

Go 有一个特殊的函数叫 init,它在包被导入时自动执行,不需要手动调用:

package main

import "fmt"

func init() {
    fmt.Println("初始化")
}

func main() {
    fmt.Println("主函数")
}

输出:

初始化
主函数

init 函数特点:

  • 没有参数和返回值
  • 一个文件可以有多个 init,按声明顺序执行
  • 不能被手动调用
  • 主要用于初始化全局变量、注册驱动等
Note

执行顺序:包级变量初始化 -> init 函数 -> main 函数。

实际示例

计算器

func calculate(op string, a, b float64) (float64, error) {
    switch op {
    case "+":
        return a + b, nil
    case "-":
        return a - b, nil
    case "*":
        return a * b, nil
    case "/":
        if b == 0 {
            return 0, errors.New("除数不能为零")
        }
        return a / b, nil
    default:
        return 0, fmt.Errorf("不支持的操作:%s", op)
    }
}

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

多返回值交换变量

func swap(a, b int) (int, int) {
    return b, a
}

func main() {
    x, y := 1, 2
    x, y = swap(x, y)
    fmt.Println(x, y)  // 2 1
}

当然,Go 直接支持多重赋值,不需要函数:

x, y = y, x  // 直接交换

常见问题

忘记处理 error

result, _ := divide(10, 0)  // 忽略错误
fmt.Println(result)         // 输出 0,但不知道出错了

养成习惯:每个返回 error 的函数都要检查。

命名返回值误用

func bad() (result int) {
    if cond {
        result = 10
        // 忘了 return,继续执行
    }
    // result 还是零值
    return  // 返回 0 而不是 10
}

命名返回值的裸 return 容易出这种 bug。在复杂逻辑中,显式 return result 更安全。

参数太多

如果函数有超过 5 个参数,考虑用结构体打包:

// 不推荐
func createUser(name string, age int, email string, phone string, city string, country string) {}

// 推荐
type UserConfig struct {
    Name    string
    Age     int
    Email   string
    Phone   string
    City    string
    Country string
}

func createUser(cfg UserConfig) {}

学完这节你能做什么

  • 声明和调用函数
  • 使用多返回值处理错误
  • 用命名返回值提高可读性
  • 把函数作为值传递
  • 知道 Go 不支持函数重载

下一节讲变参函数。