Go函数声明必须显式写出参数和返回类型,不支持类型推导;调用时实参个数、顺序、类型须完全匹配;参数均为值传递,但切片/映射/通道/指针因内部含间接引用而表现特殊;函数是一等公民,类型由签名严格定义,闭包捕获变量引用而非快照。

函数声明必须显式写出参数和返回类型
Go 不支持类型推导的函数签名,func 关键字后必须紧跟着参数名和类型(类型在后),再是返回类型。漏写任一类型都会编译报错。
常见错误:把参数类型写在前面(像 C 或 Java 那样)——func add(int a, int b) int 是非法的,正确写法是 func add(a int, b int) int。
多个同类型参数可简写:func max(a, b, c int) int 等价于 func max(a int, b int, c int) int。
返回值也支持命名,例如 func split(n int) (x, y int),此时函数体内可直接用 x、y 赋值,且 return 可不带参数(“裸返回”),但仅限命名返回值且逻辑清晰时使用,否则易引发歧义。
立即学习“go语言免费学习笔记(深入)”;
调用时不能省略参数个数或类型匹配
Go 是强类型语言,函数调用时传入的实参个数、顺序、类型必须与声明完全一致。不存在默认参数、重载或可变参数自动补全。
可变参数用 ... 表示,但只能放在参数列表末尾,例如 func sum(nums ...int)。调用时可传 sum(1, 2, 3) 或 sum([]int{1,2,3}...);若传切片不加 ...,会报类型不匹配错误。
常见坑点:
- 误将切片直接传给
...T参数(缺少尾部...) - 在已有命名返回值的函数里裸
return,却忘了初始化某个返回变量,导致返回零值而不报错 - 函数有多个返回值时,用
_忽略部分值,但顺序不能错,例如_, err := os.Open("x")合法,err, _ := os.Open("x")则可能因类型错位而编译失败
参数传递始终是值拷贝,但切片/映射/通道/指针行为特殊
Go 所有函数参数都是值传递:每次调用都复制实参内容。但复制的内容可能是“指向底层数据的结构体”,所以效果看起来像引用传递。
例如:
-
[]int是一个包含len、cap和指向底层数组的指针的结构体,传参时复制该结构体,因此对切片元素的修改会影响原切片,但append后若扩容,新底层数组不会影响调用方 -
map和chan同理,内部是轻量句柄,复制后仍指向同一底层资源 -
*T传的是指针值的拷贝,但解引用后操作的是同一块内存 - 纯
struct、string、int等则真正复制全部内容,函数内修改不影响外部
别误以为 “Go 有引用传递”——它只有值传递,只是某些类型的值本身包含间接引用。
函数是一等公民,可赋值、传参、返回,但类型必须严格匹配
函数类型由其签名定义:func(int, string) bool 是一种独立类型,两个签名稍有不同(如参数名不同、返回名不同)就不是同一类型,不能互相赋值。
典型用法:
- 变量声明:
var f func(int) string = myFunc - 作为参数:
func doWork(cb func(string) error) { ... } - 匿名函数立即调用:
func(x int) { fmt.Println(x) }(42)
注意:函数类型不支持比较(除了与 nil),也不能用作 map 的 key。若需缓存函数逻辑,常用方式是用字符串 key 映射到函数变量,而非直接拿函数本身做 key。
最常被忽略的一点:闭包捕获的是变量的引用,不是快照。如果在循环中创建多个匿名函数并引用循环变量(如 for i := range s { go func() { println(i) }() }),所有 goroutine 最终可能打印同一个值——必须显式传参或用局部变量绑定。










