在实际开发中, 总有一些函数的参数个数是在编码过程中无法确定的, 比如我们最常用的 fmt.Printf 和 fmt.Println:
- fmt.Printf("一共有 %v 行 %v 列 \ n", rows, cols)
- fmt.Println("共计大小:", size)
当你需要实现类似的接口时, 就需要我们的不定参数出场了.
golang 的不定参数
顾名思义, 不定参数就是一个占位符, 你可以将 1 个或者多个参数赋值给这个占位符, 这样不管实际参数的数量是多少, 都能交给不定参数来处理, 我们看一下不定参数的声明:
- func Printf(format string, a ...interface{
- }) (n int, err error)
- func Println(a ...interface{
- }) (n int, err error)
不定参数使用 name ...Type 的形式声明在函数的参数列表中, 而且需要是参数列表的最后一个参数, 这点与其他语言类似;
不定参数在函数中将转换为对应的[]Type 类型, 所以我们可以像使用 slice 时一样来获取传给函数的参数们;
有一点值得注意, golang 的不定参数不需要强制绑定参数的出现.
举个例子, 我想在 c 语言中实现一个求和任意个整数的函数 sum:
- int sum(int num, ...) {
- // todo
- }
我们只有先指定至少一个非不定参数的形参 (num) 才能使用... 不定参数, 在 golang 中是不需要这样做的:
- func sum(nums ...int) int {
- //todo
- }
这也是 golang 语法简洁的其中一个体现.
传递参数给... 不定参数
传递参数给带有不定参数的函数有两种形式, 第一种与通常的参数传递没有什么区别, 拿上一节的 sum 举个例子:
- sum(1, 2, 3)
- sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
除了参数的个数是动态变化的之外和普通的函数调用是一致的.
第二种形式是使用... 运算符以变量... 的形式进行参数传递, 这里的变量必须是与不定参数类型相同的 slice, 而不能是其他类型(没错, 数组也不可以), 看个例子:
- numbers := []int{
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
- }
- sum(numbers...) // 和 sum(1, 2, 3, 4, 5, 6, 7, 8, 9. 10)等价
这种形式最常用的地方是在内置函数 append 里:
- result := []int{
- 1, 3
- }
- data := []int{
- 5, 7, 9
- }
- result = append(result, data...) // result == []int{
- 1, 3, 5, 7, 9
- }
是不是和 python 的解包操作很像, 没错, 大部分情况下你可以把... 运算符当做是 golang 的 unpack 操作, 不过有几点不同还是要注意的:
第一, 只能对 slice 类型使用... 运算符:
- arr := [...]int{
- 1, 2, 3, 4, 5
- }
- sum(arr...) // 编译无法通过
你会见到这样的报错信息: cannot use arr (type [5]int) as type []int in argument to sum
这是因为不定参数实际是个 slice,... 运算符是个语法糖, 它把前面的 slice 直接复制给不定参数, 而不是先解包成独立的 n 个参数再传递, 这也是为什么我只说... 运算符看起来像 unpack 的原因.
第二个需要注意的地方是不能把独立传参和... 运算符混用, 再看个例子:
- slice := []int{
- 2, 3, 4, 5
- }
- sum(1, slice...) // 无法通过编译
这次你会见到一个比较长的报错:
- too many arguments in call to sum
- have (number, []int...)
- want (...int)
这是和前面所说的原因是一样的,... 运算符将不定参数直接替换成了 slice, 这样就导致前一个独立给出的参数不再算入不定参数的范围内, 使得函数的参数列表从 (...int) 变成了(int, ...int), 最终使得函数类型不匹配编译失败.
正确的做法也很简单, 不要混合使用... 运算符给不定参数传参即可.
读了这篇文章, 再加上一些简单的联系, 我相信你们一定也能掌握 golang 不定参数的使用.
参考:
- https://golang.org/ref/spec#Passing_arguments_to_..._parameters
- https://golang.org/doc/effective_go.html#append
来源: https://www.cnblogs.com/apocelipes/p/9861315.html