Go函数签名必须显式声明参数和返回类型,不支持类型推导;多返回值需全接收或用_丢弃;指针传递本质是值传递;推荐泛型替代interface{}以提升类型安全。

函数定义必须显式声明参数类型和返回类型
Go 不支持类型推导的函数签名,func 关键字后必须紧跟着参数名、类型对,再是返回类型。漏写任意一个类型都会编译失败。
常见错误:把 func add(a, b int) int 误写成 func add(a, b) int(缺少参数类型),或 func add(a int, b int) (缺少返回类型)——后者会报 missing function body,实际是语法不完整。
- 多个同类型参数可合并写法:
a, b int等价于a int, b int - 返回类型若为单个,可省略括号;多个则必须用括号包裹,如
(int, error) - 命名返回值(如
func split(x int) (a, b int))会让返回语句更简洁,但需注意:未赋值的命名返回值会取对应类型的零值
多返回值必须用括号包裹且调用时需显式接收
Go 常用多返回值表达结果与错误(如 value, err := strconv.Atoi("42")),但语法上不允许“忽略部分返回值”——除非用 _ 显式丢弃。
容易踩的坑:直接写 strconv.Atoi("42") 而不接收返回值,编译通过但结果被丢弃;更危险的是只接收一个值,如 v := strconv.Atoi("42"),这会触发编译错误:multiple-value strconv.Atoi() in single-value context。
立即学习“go语言免费学习笔记(深入)”;
- 正确接收两个值:
v, err := strconv.Atoi("42") - 只关心错误:
_, err := strconv.Atoi("42") - 命名返回值函数中,即使使用
return(无参数),也会自动返回已命名变量的当前值
指针参数不是“引用传递”,而是“传指针值”
Go 只有值传递。所谓“通过指针修改原变量”,本质是把地址这个整数值复制了一份传进去。因此 *T 参数能修改调用方变量内容,但无法修改其地址本身。
典型误判场景:想在函数内让外部指针指向新分配对象,却忘了必须传 **T 或返回新指针。
func updatePtr(p *int) {
newInt := 42
p = &newInt // ❌ 这里只改了形参 p 的副本,不影响调用方
}
func correctUpdate(p **int) {
newInt := 42
*p = &newInt // ✅ 修改调用方指针所存的地址
}
- 修改结构体字段常用
*Struct参数,避免拷贝大对象 - 切片虽是引用类型,但底层数组指针+长度+容量三者是值传递;追加元素可能触发扩容,此时原切片不受影响,需返回新切片
空接口 interface{} 和泛型(Go 1.18+)处理不确定参数的差异
旧代码常用 interface{} 模拟“任意类型”,但每次使用前需类型断言,易出 panic;Go 1.18 引入泛型后,应优先用约束更明确的类型参数。
比如实现一个通用最大值函数:Max 若用 interface{},调用时要反复断言;而用泛型 func Max[T constraints.Ordered](a, b T) T,编译期即校验类型合法性,且无运行时开销。
-
interface{}适合真正动态场景(如 JSON 解析、日志打印),但务必做安全断言:v, ok := x.(string) - 泛型函数定义需导入
constraints包(golang.org/x/exp/constraints,或 Go 1.22+ 直接用comparable/ordered) - 不要为简单逻辑强行泛型化——类型参数带来可读性成本,仅当复用价值高且类型安全收益明显时才启用










