本节介绍变量参数的使用。正确使用变量参数可以使您的代码更简单、更易于使用,特别是对于输入/输出函数(例如日志记录函数)。
变量参数类型
可变参数是指函数传递的参数个数是可变的。为此,我们首先需要将函数定义为可以接受可变参数的类型。
func myfunc(args ...int){
for _, arg := range args {
fmt.Println(arg)
}
} 上面的代码意味着函数 myfunc() 接受可变数量的参数(均为 int 类型),因此您可以这样调用它:
myfunc(2, 3, 4)
myfunc(1, 3, 7, 13)
...type形式的类型只能作为函数的参数类型存在,并且必须是最后一个参数。这是语法糖。换句话说,这种语法对函数的函数没有影响。程序员通常使用语法糖来增加程序的可读性,从而减少程序出错的机会。
从内部实现机制来看, type ...type本质上是一个数组切片,即[]type 。所以上面的参数args可以使用for循环来获取每个传入的参数。
如果没有...type的语法糖,开发人员将不得不编写:
func myfunc2(args []int) {
for _, arg := range args {
fmt.Println(arg)
}
} 从功能实现的角度来看,这没有任何作用。你可以随心所欲地写它,但是从调用者的角度来看,情况完全不同。
myfunc2([]int{1, 3, 7, 13})
事实证明,您需要添加[]int{}来构造数组切片实例,但使用...type语法糖您不必自己执行此操作。
任何类型的可变参数
在前面的示例中,变量参数类型限制为 int。如果要传递任何类型,可以将类型指定为interface{}。下面是 Go 标准库中 fmt.Printf() 的函数原型。 :
func Printf(format string, args …interface{}) {
// …
}
使用 Interface{} 传递任何类型的数据是 Go 语言的常见用法。使用interface{}仍然是类型安全的,但这与C/C++不同。让我们通过一个例子来了解如何分配接口{}类型的数据。
package main
import "fmt"
func MyPrintf(args ...interface{}) {
for _, arg := range args {
switch arg.(type) {
case int:
fmt.Println(arg, "is an int value.")
case string:
fmt.Println(arg, "is a string value.")
case int64:
fmt.Println(arg, "is an int64 value.")
default:
fmt.Println(arg, "is an unknown type.")
}
}
}
func main() {
var v1 int = 1
var v2 int64 = 234
var v3 string = "hello"
var v4 float32 = 1.234
MyPrintf(v1, v2, v3, v4)
}该程序的输出是:
1 is an int value.
234 is an int64 value.
hello is a string value.
1.234 is an unknown type.
遍历可变参数列表——获取每个参数的值
可变参数列表的数量不固定,传入的参数是切片。如果需要获取每个参数的具体值,可以扫描可变参数变量。请参阅下面的代码。
package main
import (
"bytes"
"fmt"
)
// 引数の数は0〜nであり、型制約は文字列である関数を定義する
func joinStrings(slist ...string)string {
// バイトバッファを定義して、文字列を高速に接続する
var b bytes.Buffer
// 可変引数リスト([]string型)を繰り返します。
for _, s:= range slist {
// 反復処理された文字列をバイト配列に連続して書き込みます
b.WriteString(s)
}
// 接続されたバイト配列を文字列に変換し、出力します。
return b.String()
}
func main(){
// 3つの文字列を入力し、1つの文字列に連結します
fmt.Println(joinStrings("pig "、"and"、"rat"))
fmt.Println(joinStrings("hammer"、"mom"、"and"、"hawk"))
} 这是代码输出:
pig and rat
hammer mom and hawk
代码说明如下:
- 第 8 行定义了一个具有可变参数的函数。 slist 的类型为 []string,每个参数的类型为 string。也就是说,该函数只接受字符串类型作为参数。
- 第 11 行的 bytes.Buffer 的行为类似于本示例中的 StringBuilder,可以有效地执行字符串连接操作。
- 第 13 行检查 slist 的变量参数。 s为各参数的值,类型为字符串。
- 第 15 行将每个传入参数放入 bytes.Buffer 中。
- 第 19 行将 bytes.Buffer 中的数据转换为字符串,并将其作为函数的返回值返回。
- 在第 24 行,输入三个字符串并使用 joinStrings() 函数将参数连接到字符串输出。
- 第25行输入4个字符的字符串,连接后输出。
如果想获取可变参数的个数,可以使用len()函数计算可变参数的变量对应的切片长度,得到可变参数的个数。
get variadic type – 获取每个参数的类型
如果变量参数的类型为interface{},则可以传递任何类型的值。这时如果需要获取变量的类型,可以通过switch来获取变量的类型。以下代码将一系列不同类型的值传递给 printTypeValue() 函数。打印每个不同参数的值和类型的详细说明。
打印类型和值:
package main
import(
"bytes"
"fmt"
)
func printTypeValue(slist ...interface{}) string {
//バイトバッファを高速文字列接続として使用します
var b bytes.Buffer
//パラメータを繰り返します
for _, s := range slist {
//interface {}タイプを文字列にフォーマットします
str := fmt.Sprintf("%v"、s)
//タイプの文字列説明
var typeString string
//sの型をアサーションします
switch s.(type) {
case bool:// sがブール型の場合
typeString = "bool"
case string:// sが文字列型の場合
typeString = "string"
case int:// sが整数型の場合
typeString = "int"
}
//文字列接頭辞を書く
b.WriteString("value: ")
//値を書き込む
b.WriteString(str)
//タイプ接頭辞を書く
b.WriteString(" type: ")
//タイプ文字列を書き込む
b.WriteString(typeString)
//改行を書き込む
b.WriteString("\ n")
}
return b.String()
}
func main() {
//printTypeValue()を介して異なるタイプの変数を印刷します
fmt.Println(printTypeValue(100、 "str"、 true))
} 这是代码输出:
value: 100 type: int
value: str type: string
value: true type: bool
代码说明如下:
- 第8行的printTypeValue()接受各种类型的值并打印类型和值的描述。
- 第 11 行,bytes.Buffer 字节作为快速字符串连接进行缓冲。
- 第 14 行,扫描 slist 的每个元素。类型是接口{}。
- 第 17 行使用 fmt.Sprintf 和
%v动词将 interface{} 形式的值转换为字符串。 - 第 20 行声明一个字符串作为变量类型名称。
- 第 23 行的 switch s.(type) 可以对 interface{} 类型执行类型断言。即确定变量的实际类型。 {}
- 第24到29行展示了s变量可能的类型,每个类型对应的类型字符串被赋值给typeString。
- 第 33 至 42 行描述了输出格式。

可变参数变量是包含所有参数的切片。如果要将这个可变参数变量传递给下一个可变参数函数,可以在传递时在可变参数变量后面添加...来传递该元素。传递的是切片而不是可变参数本身。
下面的示例模拟 print() 函数和实际调用的 rawPrint() 函数。这两个函数都有必须从 print 传递到 rawPrint 的可变参数。
传递变量参数:
package main
import "fmt"
//実際に印刷される関数
func rawPrint(rawList ...interface{}) {
//可変パラメーターセットを反復処理
for _, a := range rawList {
//パラメータを印刷する
fmt.Println(a)
}
}
//印刷関数の包装
func print(slist ...interface{}) {
// slistを次の関数に完全に渡すために可変引数セットをrawPrintに渡します
rawPrint(slist...)
}
func main() {
print(1, 2, 3)
} 这是代码输出:
1
2
3
代码解释:
- 第9至13行扫描并输出rawPrint()的参数列表rawList。
- 在第 20 行,将
...添加到 print 的变量参数列表后,该变量被传递给 rawPrint()。 - 在第 25 行,传递三个整数值 1、2、3 并打印它们。
如果我尝试将第 20 行更改为:
rawPrint("fmt", slist) 再次运行代码将输出以下内容:
[1 2 3]
此时,slist(type []interface{})整体传递给rawPrint(),rawPrint()函数内部传递的变量也成为slist的切片值。
使用...传递的变量参数与使用切片之间的附加连接具有相同的功能。




![2021 年如何设置 Raspberry Pi Web 服务器 [指南]](https://i0.wp.com/pcmanabu.com/wp-content/uploads/2019/10/web-server-02-309x198.png?w=1200&resize=1200,0&ssl=1)

