Go语言函数基本组成:关键字func、函数名、参数列表、返回值、函数体和返回语句。
func 函数名(参数列表) (返回值列表) {
// 函数体
return
}
除了main()
、init()
函数外,其它所有类型的函数都可以有参数与返回值。对于函数,一般也可以这么认为:"func" FunctionName Signature [ FunctionBody ] 。"func" 为定义函数的关键字,FunctionName 为函数名,Signature 为函数签名,FunctionBody 为函数体。以下面定义的函数为例:
func FunctionName (a typea, b typeb) (t1 type1, t2 type2)
函数签名由函数参数、返回值以及它们的类型组成,被统称为函数签名。如:
(a typea, b typeb) (t1 type1, t2 type2)
如果两个函数的参数列表和返回值列表的变量类型能一一对应,那么这两个函数就有相同的签名,下面testa与testb具有相同的函数签名。
func testa (a, b int, z float32) bool
func testb (a, b int, z float32) (bool)
函数调用传入的参数必须按照参数声明的顺序。而且Go语言没有默认参数值的说法。函数签名中的最后传入参数可以具有前缀为....的类型(...int),这样的参数称为可变参数,并且可以使用零个或多个参数来调用该函数,这样的函数称为变参函数。
func doFix (prefix string, values ...int)
函数的参数和返回值列表始终带括号,但如果只有一个未命名的返回值(且只有此种情况),则可以将其写为未加括号的类型;一个函数也可以拥有多返回值,返回类型之间需要使用逗号分割,并使用小括号 () 将它们括起来。
func testa (a, b int, z float32) bool
func swap (a int, b int) (t1 int, t2 int)
在函数体中,参数是局部变量,被初始化为调用者传入的值。函数的参数和具名返回值是函数最外层的局部变量,它们的作用域就是整个函数。如果函数的签名声明了返回值,则函数体的语句列表必须以终止语句结束。
func IndexRune(s string, r rune) int {
for i, c := range s {
if c == r {
return i
}
}
return // 必须要有终止语句,如果这里没有return,则会编译错误:missing return at end of function
}
函数重载(function overloading)指的是可以编写多个同名函数,只要它们拥有不同的形参或者不同的返回值,在 Go 语言里面函数重载是不被允许的。
函数也可以作为函数类型被使用。函数类型也就是函数签名,函数类型表示具有相同参数和结果类型的所有函数的集合。函数类型的未初始化变量的值为nil。就像下面:
type funcType func (int, int) int
上面通过type关键字,定义了一个新类型,函数类型 funcType 。
函数也可以在表达式中赋值给变量,这样作为表达式中右值出现,我们称之为函数值字面量(function literal),函数值字面量是一种表达式,它的值被称为匿名函数,就像下面一样:
f := func() int { return 7 }
下面代码对以上2种情况都做了定义和调用:
package main
import (
"fmt"
"time"
)
type funcType func(time.Time) // 定义函数类型funcType
func main() {
f := func(t time.Time) time.Time { return t } // 方式一:直接赋值给变量
fmt.Println(f(time.Now()))
var timer funcType = CurrentTime // 方式二:定义函数类型funcType变量timer
timer(time.Now())
funcType(CurrentTime)(time.Now()) // 先把CurrentTime函数转为funcType类型,然后传入参数调用
// 这种处理方式在Go 中比较常见
}
func CurrentTime(start time.Time) {
fmt.Println(start)
}
下一节:Go 语言中函数默认使用按值传递来传递参数,也就是传递参数的副本。函数接收参数副本之后,在使用变量的过程中可能对副本的值进行更改,但不会影响到原来的变量。