golang语言 函数
形参(或结果)列表中,名称要么都存在,要么都不存在
指定了返回形参的名字,在进入函数时,返回值会被初始化为自身类型的零值
若所有的返回值都有名称,return语句可以省略操作数,这被称为bare return
若只有一个没有名称的结果,结果列表可以不加括号
未初始化的函数类型变量的值为nil,调用nil值的函数会引发panic
函数不能比较,可以和nil比较
实参在函数调用前求值
形参都是值传递的,不存在传引用,切片传的也是值
函数的类型被称为函数的标识符。如果两个函数形式参数列表和返回值列表中的变量类型一一对应,那么这两个函数被认为有相同的类型和标识符。
若函数g的返回值和f的形参符合,那么可以 f( g() ),
g的返回值不只1个,那么g()不能和其他参数一起作为f的实参,f( 3, g() )是错误的
g只有1个返回值,g()可以和其他参数一起作为f的实参,f(3,4,g(),5,6)是可以的
return &File{fd: fd, name: name} // 可返回地址
func f (x int) (y int){
x := 1 // 报错
y := 1 // 报错
return 1 // x y 都没使用 ok
}
函数字面量
f := func(x, y int) int { return x + y } //赋值给变量 func(ch chan int) { ch <- ACK }(replyChan) //字面量调用
函数值
func square(n int) int { return n * n }
func product(m, n int) int { return m * n }
f := square
fmt.Println(f(3)) // "9"
f = product // compile error: can't assign func(int, int) int to func(int) int
匿名函数
拥有函数名的函数只能在包级语法块中被声明,通过函数字面量(function literal) ,我们可绕过这一限制,在任何表达式中表示一个函数值。
func squares() func() int {
var x int
return func() int {
x++
return x * x
}
}
func main() {
f := squares()
fmt.Println(f()) // "1"
fmt.Println(f()) // "4"
fmt.Println(f()) // "9"
fmt.Println(f()) // "16"
}
在squares中定义的匿名内部函数可以访问和更新squares中的局部变量,这意味着匿名函数和squares中,存在变量引用。这就是函数值属于引用类型和函数值不可比较的原因。
递归调用匿名函数,需要先声明变量,再将匿名函数赋值给变量,必须分成两步,否则报错。
var f func(i int) int
f = func(i int) int {
if i == 0{ return 0 }
return i + f(i-1)
}
f(3) // 3+2+1+0==6
可变参数
最后一个形参的类型前可能带有...前缀,表示接受零个或多个实参,
在函数体中,可变参数被看作slice
在slice后加上...,可将slice作为实参传给可变参数
可变参数和切片参数是不同的
func f(...int) {} // func(...int)
func g([]int) {} // func([]int)
panic
函数 F 中的 panic 调用会终止 F 的执行。 任何在 F 中 defer 的函数都会在 F 返回给其调用者前执行,且不执行函数F剩余的部分(panic后面的defer也不会被执行)。其调用者 G 对 F 的调用如同对 panic 的调用,即终止G的执行, 并以相同的方式运行被推迟的函数。这会持续到该Go程中所有函数都按相反顺序停止执行之后。 到那时,该程序将被终止,而该错误情况会被报告,包括引发 panic 的实参值。 此终止序列称为恐慌过程。
defer fmt.Println("in main") defer func() { defer func() { panic("panic again and again") }() panic("panic again") }() panic("panic once")
打印:
in main
panic: panic once
panic: panic again
panic: panic again and again
recover
recover 函数允许程序管理恐慌过程Go程的行为。在defer的函数中,执行 recover() 调用会通过恢复正常执行, 并取回传递至 panic 调用的错误值来停止恐慌过程序列。导致panic的函数不会继续运行,但能正常返回。panic后面的任何代码,包括后面的defer和recover等,都不会执行。
若 recover 在已推迟函数之外被调用,它将不会停止恐慌过程序列。在此情况下,或当Go程不在恐慌过程中时, 或提供给 panic 的实参为 nil 时,recover 就会返回 nil,多次调用recover,除第一次执行的recover以外,其他recover都返回nil
func protect(g func()) { defer func() { if x := recover(); x != nil { log.Printf("run time panic: %v", x) } }() g() } defer recover() // defer 直接调用 recover(),无效 defer func() { func() { recover() }() // defer 嵌套调用 recover(),无效 }()
defer
defer的函数在外层函数执行到末尾,或者执行了return语句,或者panic后执行
defer语句执行时,defer的函数值与形参会被求值并保存
defer的函数按被defer的相反顺序执行
defer的函数 f() 如果是nil值的函数,那么在defer f()时不会panic,在执行 f 时会panic
for i := 0; i <= 3; i++ { defer fmt.Print(i) // 3 2 1 0 } for i := 0; i <= 3; i++ { defer func(){ // 4 4 4 4 fmt.Println(i) }() } for i := 0; i <= 3; i++ { defer func( i int ){ // 3 2 1 0 fmt.Println(i) } ( i ) } func f() (result int) { // f 返回 1 defer func() { result++ }() return 0 } func(){ // 打印 a f := func(){ fmt.Println("a") } defer f() // 此时 f 已经求值 f := func(){ fmt.Println("b") } } func NeverExit(name string, f func()) { // panic时,重启goroutine defer func() { if v := recover(); v != nil { go NeverExit(name, f) } }() f() } -------------------------------------------------------------------------------- for _, v := range arr { go func() { process( v ) // for所在的go程和多个新go程共享和修改v }() } for _, v := range arr { go func(v int) { process( v ) }(v) //复制了v的副本 } for _, v := range arr { v := v //复制了v的副本,局部变量屏蔽循环变量,常见写法 go func() { process( v ) }() } -------------------------------------------------------------------------------- var rmdirs []func() for _, dir := range tempDirs() { rmdirs = append(rmdirs, func() { os.RemoveAll(dir) // 错误 }) } dirs := tempDirs() for i := 0; i < len(dirs); i++ { rmdirs = append(rmdirs, func() { os.RemoveAll(dirs[i]) // 错误 }) }