函数定义
函数的基本组成:关键字func,函数名,参数列表,返回值,函数体,返回语句。 示例如下:
代码语言:javascript复制func Add(a int, b int) (ret int, err error) {
if a < 0 || b < 0 {
err = errors.New("should be non-negative numbers")
return
}
return a b, nil // 支持多重返回值
}
如果参数列表中若干个相邻的参数类型相同,可以省略前面参数的类型声明:
代码语言:javascript复制// 参数列表中a,b的类型相同,省略了a参数的类型声明
func Add(a, b int) (ret int, err error) {
// ...
}
如果返回值列表中多个返回值的类型相同,也可以使用同样的方式合并。
如果函数返回值只有一个,不用声明返回值变量名:
代码语言:javascript复制// 函数只有一个返回值,不用声明返回值变量名
func Add2(a, b int) int {
// ...
}
函数调用
在调用函数前,需要导入函数所在的包:
代码语言:javascript复制import "mymath"// 假设Add被放在一个叫mymath的包中
c := mymath.Add(1, 2)
注意: Golang中函数名字的首字母大小写不仅仅是风格,更直接体现了该函数的可见性。 规则:小写字母开头的函数只在本包内可见,大写字母开头的函数才能被其他包使用。这个规则也适用于类型和变量的可见性。
不定参数
不定参数是指函数的参数个数为不定数量。
不定参数类型
将函数定义为接收不定参数类型:
代码语言:javascript复制func myFunc(args ...int) {
for _, arg := range args {
fmt.Print(arg, " ")
}
fmt.Println()
}
调用:
代码语言:javascript复制// 传递给函数的参数个数不固定
myFunc(1)
myFunc(1, 2)
形如...type
格式的类型只能作为函数的参数类型存在,并且必须是最后一个参数。
不定参数的传递
假设有一个变参函数myFun2
:
func myFun2(args ...int) {
// ...
}
如下示例展示了如何向其传递变参:
代码语言:javascript复制func myFunc(args ...int) {
// 按原样传递
myFun2(args...)
// 传递参数片段
myFun2(args[1:]...)
}
任意类型的不定参数
如果希望传递任意类型的参数,可以指定类型为interface{}
。
示例:
// 不定参数类型
func myPrintf(args ...interface{}) {
for _, arg := range args {
switch arg.(type) {
case int:
fmt.Println(arg, "is an integer")
case string:
fmt.Println(arg, "is a string")
case int64:
fmt.Println(arg, "is a int64")
default:
fmt.Println(arg, "is an unknown type")
}
}
}
func main() {
var v1 int = 1
var v2 int64 = 2
var v3 string = "hello"
var v4 float32 = 1.23
// 调用不定参数类型函数
myPrintf(v1, v2, v3, v4)
}
输出:
代码语言:javascript复制1 is an integer
2 is a int64
hello is a string
1.23 is an unknown type
多返回值
Golang函数或者成员的方法可以有多个返回值,这个特性能够使我们写出比其他语言更优雅、更简洁的代码。
比如File.Read()
函数就可以同时返回读取的字节数和错误信息,如果读取文件成功,则返回值中的n为读取的字节数,err为nil,否则err为具体的出错信息:
func (file *File) Read(b []byte) (n int, err Error)
还可以给返回值命名,就像函数的输入参数一样。 返回值被命名之后,它们的值在函数开始的时候被自动初始化为空。 在函数中执行不带任何参数的return语句时,会返回对应的返回值变量的值。
Golang并不需要强制命名返回值,但是命名后的返回值可以让代码更清晰,可读性更强,同时也可以用于文档。
如果调用方调用了一个具有多返回值的方法,但是却不想关心其中的某个返回值,可以简单地用一个下划线“_”来跳过这个返回值。
比如下面的代码表示调用者在读文件的时候不想关心Read()
函数返回的错误码:
n, _ := f.Read(buf)
匿名函数和闭包
匿名函数是指不需要定义函数名的一种函数实现方式。
匿名函数
在Golang中,函数可以像普通变量一样被传递或使用,可以随时在代码里定义匿名函数。
匿名函数由一个不带函数名的函数声明和函数体组成,如下所示:
代码语言:javascript复制func(a, b int, z float64) bool {
return a*b <int(z)
}
匿名函数可以直接赋值给一个变量或者直接执行:
代码语言:javascript复制// 定义匿名函数并赋值给变量
f := func(x, y int) int {
return x y
}
// 调用函数
f(1, 2)
// 匿名函数的花括号后面直接跟参数列表表示调用匿名函数
func(x, y int) int {
return x y
}(1,2)
闭包
Golang的匿名函数是一个闭包。 Golang中的闭包同样也会引用到函数外的变量,闭包的实现确保只要闭包还被使用,那么被闭包引用的变量会一直存在。
代码语言:javascript复制// 闭包
var j int = 5
a := func() {
var i int = 10
fmt.Printf("i, j: %d, %dn", i, j)
}
a()
j *= 2
a() // 在这里还会调用闭包函数,被它引用的外部变量j一直存在,所以其只会在两次闭包调用时不同
输出:
代码语言:javascript复制i, j: 10, 5 # 第一次调用闭包函数时,变量j的值未变化
i, j: 10, 10 # 第二次调用闭包函数时,变量j的值已经发生变化