函数:把重复代码收起来
本章目标:把重复逻辑收进函数里,做到三件事就算过关:会写函数、会传参数、会接返回值。

常见坑位
- 代码还没重复,就提前拆一堆函数,最后自己都看不懂。
- 函数写好了,但忘了在
main里调用,运行后啥都没发生。 - 参数和返回值方向搞反:把"输入"写成"输出"。
- 看见多返回值就紧张,其实就是"一个函数一次带回多个结果"。
- 误以为函数里改了变量,外面一定会变。Go 默认是值传递,不一定会变。
先跑一个最小示例
先跑起来,再理解细节,这是最省时间的学习方式。
go
package main
import "fmt"
func sayHello() {
fmt.Println("你好,欢迎学习 Go")
}
func main() {
sayHello()
sayHello()
}
看到两行输出就说明你已经会"定义 + 调用"函数了。
带参数的函数
参数可以理解成"你喂给函数的数据"。
go
package main
import "fmt"
func sayHi(name string) {
fmt.Println("你好,", name)
}
func main() {
sayHi("小张")
sayHi("小刘")
}
这里 name string 的意思是:这个函数接收一个字符串类型的名字。
带返回值的函数
返回值就是"函数做完活后交回来的结果"。
go
package main
import "fmt"
func add(a int, b int) int {
return a + b
}
func main() {
result := add(3, 5)
fmt.Println(result) // 8
}
你可以把 add 理解成一个小工具:给它两个数,它回你一个和。
多返回值:Go 的高频写法
Go 很常见的一个特点是:一个函数可以返回多个值。
go
package main
import "fmt"
func divide(a int, b int) (int, string) {
if b == 0 {
return 0, "不能除以零"
}
return a / b, ""
}
func main() {
result, msg := divide(10, 2)
fmt.Println(result, msg) // 5
result2, msg2 := divide(10, 0)
fmt.Println(result2, msg2) // 0 不能除以零
}
后面你会大量见到这种形式:
go
value, err := someFunc()
前面是结果,后面是错误信息。下一章我们专门讲这个模式。
变长参数:参数个数不固定
当你不确定要传几个参数时,用 ...。
go
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
fmt.Println(sum(10, 20, 30, 40)) // 100
}
nums ...int 在函数内部本质上就是 []int,这点后面学切片时会越用越顺。
匿名函数:临时用一次,不想起名
匿名函数就是"没有名字的函数"。
go
package main
import "fmt"
func main() {
greet := func(name string) {
fmt.Println("你好,", name)
}
greet("小张")
greet("小李")
func() {
fmt.Println("这段逻辑只执行一次")
}()
}
以后在排序、HTTP 处理、goroutine 里,你会反复看到匿名函数。
闭包:匿名函数能记住外面的变量
go
package main
import "fmt"
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
func main() {
next := counter()
fmt.Println(next()) // 1
fmt.Println(next()) // 2
fmt.Println(next()) // 3
}
这里返回的匿名函数记住了 count,这就是闭包。
初学先记两点:
- 闭包可以访问外层变量。
- 闭包会让外层变量活得更久,别长期挂大对象。
函数还能当参数传来传去
Go 里函数也是"值",可以传给别的函数。
go
package main
import "fmt"
func apply(nums []int, fn func(int) int) []int {
result := make([]int, 0, len(nums))
for _, n := range nums {
result = append(result, fn(n))
}
return result
}
func main() {
doubled := apply([]int{1, 2, 3}, func(n int) int {
return n * 2
})
fmt.Println(doubled) // [2 4 6]
}
fn func(int) int 的意思是:这个参数本身是一个函数,输入 int,输出 int。
值传递和引用类型,一次讲清
Go 只有值传递。
- 传
int、string、结构体:函数里改变量,外面通常不变。 - 传
slice、map:拷贝的是结构头,但底层数据共享,改元素外面能看到。
所以"函数里改了,外面会不会变",要看你传入的具体类型。
过关标准
你可以按这份清单自检:
- 能自己写出并调用一个无参数函数。
- 能写一个带参数函数,并传入不同数据。
- 能写一个带返回值函数,并正确接收结果。
- 看到
value, err := xxx()不再慌。 - 能说清"值传递"和"slice/map 的表现差异"。
练习任务
- 写一个
welcome(),打印一句欢迎语。 - 写一个
sayHi(name string),分别给两个名字打招呼。 - 写一个
max(a, b int) int,返回较大的那个数。 - 写一个
sum(nums ...int) int,输入任意个整数并求和。
下一章衔接
下一章我们就接着讲 Go 里最常见的错误处理模式:value, err := ... 到底该怎么写才稳。
本文章属于《Go语言大白话入门》零基础全套教程,持续更新、全程实操练手。 微信公众号【码栈研说】超前更新,目前比掘金领先 30 章内容,完整连载、无删减、持续日更,需要完整系统学习的读者可自行前往查看。
文中部分内容借助 AI 辅助梳理,全文人工校验、实操落地。