Go 函数定义和调用
函数允许你将执行特定任务的一段代码封装起来,从而让你的代码变得更加模块化、可复用且易于理解。
本章将全面覆盖如何在 Go 语言中定义和调用函数,包括函数签名、参数传递以及返回值的核心概念。
1. 定义函数
在 Go 语言中,我们使用 func 关键字来定义一个函数。紧随其后的是函数名、参数列表(用圆括号括起来)、可选的返回值列表,最后是函数体(用大括号括起来)。
1.1 基础语法解析
定义函数的通用语法如下:
func 函数名(参数1 类型1, 参数2 类型2, ...) (返回值类型1, 返回值类型2, ...) {
// 函数体代码
return 返回值1, 返回值2, ...
}让我们逐一拆解:
func关键字: 明确告诉编译器,你正在定义一个函数。函数名: 给你的函数起的名字。应该使用具有描述性的名字,让人一眼看出它在干什么。参数列表: 函数接收的输入数据。每个参数由名字和类型组成。返回值列表: 函数执行完毕后输出的数据类型。如果函数不返回任何东西,这部分可以省略。{ ... }: 函数体,包含具体的执行逻辑。return语句: 用于将结果送出函数。如果函数规定了返回值,就必须有return。
1.2 第一个简单函数示例
来看一个计算两个整数之和的简单函数:
package main
import "fmt"
// 定义一个名为 add 的函数
func add(x int, y int) int {
sum := x + y
return sum // 返回计算结果
}
func main() {
result := add(5, 3) // 调用 add 函数
fmt.Println(result) // 输出: 8
}在这个例子中,add 函数接收两个 int 类型的参数 x 和 y,并且声明了它会返回一个 int 类型的值。
1.3 参数类型简写 (Shorthand)
如果函数有多个连续的参数属于同一种数据类型,你可以只在最后一个参数后面写上类型,前面的都可以省略:
// x 和 y 都是 int 类型,可以简写为 x, y int
func add(x, y int) int {
return x + y
}这种写法能让代码看起来更加精简干练。
1.4 无参数函数
函数当然也可以不接收任何参数,这时圆括号里留空即可:
package main
import "fmt"
func greet() {
fmt.Println("Hello, world!")
}
func main() {
greet() // 调用时括号也不能少
}2. Go 语言的杀手锏:多返回值
Go 语言原生支持一个函数同时返回多个值。这是一个极其强大的特性,最典型的应用场景就是同时返回业务结果和错误信息 (Error)。
package main
import (
"fmt"
"errors"
)
// divide 函数返回两个值:计算结果 (float64) 和 错误信息 (error)
func divide(x, y float64) (float64, error) {
if y == 0 {
// 如果除数为 0,返回 0 和一个具体的错误
return 0, errors.New("除数不能为零")
}
// 计算成功,返回结果和 nil (表示没有错误)
return x / y, nil
}
func main() {
// 正常除法
result, err := divide(10.0, 2.0)
if err != nil {
fmt.Println("发生错误:", err)
return
}
fmt.Println("计算结果:", result) // 输出: 计算结果: 5
// 触发零除错误
result, err = divide(10.0, 0.0)
if err != nil {
fmt.Println("发生错误:", err) // 输出: 发生错误: 除数不能为零
return
}
fmt.Println("计算结果:", result)
}在这个例子中,接收方必须同时处理正常的返回值和可能发生的错误,这强制开发者写出更安全的防御性代码。
3. 命名返回值 (Named Return Values)
Go 还允许你在函数签名中直接为返回值命名。一旦命名,这些变量在函数刚开始执行时就会被自动初始化为零值。
当你使用命名返回值时,你可以在函数末尾使用一个不带任何参数的 “裸返回” (Naked return),它会自动将当前命名变量的值返回出去。
package main
import "fmt"
// sum 和 difference 是命名的返回值
func addAndSubtract(x, y int) (sum int, difference int) {
sum = x + y // 直接赋值给命名的返回值变量
difference = x - y
return // 裸返回:自动返回 sum 和 difference 当前的值
}
func main() {
s, d := addAndSubtract(5, 3)
fmt.Println("和:", s, "差:", d) // 输出: 和: 8 差: 2
}最佳实践警告: 虽然裸返回在短小的函数里看起来很酷,但在长函数中,它会严重破坏代码的可读性(读者很难一眼看出到底返回了什么)。因此,建议只在非常简短的函数中使用命名返回值和裸返回。
4. 调用函数与值传递 (Pass by Value)
调用函数非常简单,写出函数名并传入匹配的参数即可:multiply(4, 6)。
但你需要牢记 Go 语言中一个极其重要的底层原理:Go 语言中的函数参数传递,全部都是“值传递” (Pass by Value)。
这意味着,当你把一个变量传给函数时,Go 实际上是把这个变量的值拷贝了一份,把复印件交给了函数。函数在内部怎么折腾这份复印件,都绝对不会影响到函数外面的原件。
package main
import "fmt"
func modifyValue(x int) {
x = x * 2 // 这里修改的是 x 的复印件
fmt.Println("函数内部的值:", x) // 输出: 20
}
func main() {
value := 10
modifyValue(value) // 把 value 拷贝一份传进去
// 原件毫发无损!
fmt.Println("函数外部的值:", value) // 输出: 10
}如果你确实需要让函数修改外部变量的值,你需要使用指针 (Pointers),我们会在后续课程中专门讲解。
4.1 忽略不需要的返回值
如果一个函数返回了多个值,但你只对其中某几个感兴趣,Go 编译器不允许你声明了变量却不用。此时你需要使用空白标识符 _ 来丢弃不需要的返回值。
package main
import "fmt"
func getCoordinates() (int, int, int) {
return 10, 20, 30
}
func main() {
// 我们只需要前两个坐标,第三个丢弃
x, y, _ := getCoordinates()
fmt.Println("X:", x, "Y:", y) // 输出: X: 10 Y: 20
}