1. 函数
- 不支持嵌套、重载和默认参数
- 无需声明原型、不定长度变参、多返回值、命名返回参数、匿名函数、闭包
- 本身就是一种类型
函数调用底层分析:
- 栈区:基本数据类型一般分配到栈区。编译器存在一个逃逸分析。每个函数有独立的栈,函数执行完毕,自动销毁
- 堆区:引用数据类型一般分配在堆区
- 代码区:存放代码指令
init()
函数:每个源文件,都可以包含一个init函数,该函数会在main函数执行前,被Go运行框架调用。
- 执行顺序:全局变量定义 -> init() -> main()
1.1 参数传递
不管是指针、引用类型,还是其他类型参数,都是值拷贝传递(pass-by-value)。区别无非是拷贝目标对象,还是拷贝指针对象本身而已。
在函数调用时,会为形参和返回值分配内存空间,并将实参拷贝到形参的内存。
1.2 参数过多,改用struct
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| type serverOption struct { ip string port int path string timeout time.Duration log *log.Logger }
func newOption() *serverOption { return &serverOption{ ip: "0.0.0.0", port: 8080, path: "/data/www", timeout: time.Second*5, log: nil, } }
func server(option *serverOption) {}
func main() { opt := newOption() opt.port = 8080
server(opt) }
|
1.3 变参
变参,实际上传递的是一个slice,如果是array,先转化为slice。s := a[:]...
1 2 3 4 5 6 7 8 9 10
| func test(a ...int) { fmt.Printf("%T, %v\n", a, a) }
func main() { test(1, 2, 3, 4) a := [3]int {10, 20, 30} test(a[:]...) }
|
2. 匿名函数
2.1 直接执行
1 2 3 4 5
| func main() { func (s string) { println(s) } ("Hello world") }
|
2.2 赋值给变量
1 2 3 4 5 6 7
| func main() { add := func (x, y int) int { return x + y }
println(add(2, 3)) }
|
2.3 作为参数
1 2 3 4 5 6 7 8 9
| func test(f func()) { f() }
func main() { test(func() { println("Hello world") }) }
|
2.4 作为返回值
1 2 3 4 5 6 7 8 9 10
| func test(x, y int) func() int { return func() int { return x + y } }
func main() { add := test(2, 3) println(add()) }
|
2.5 作为结构体字段
1 2 3 4 5 6 7 8 9 10 11 12 13
| func testStruct() { type calc struct { mul func(x, y int) int }
z := calc { mul: func(x, y int) int { return x * y }, }
println(z.mul(2, 5)) }
|
2.6 通过Channel传递
1 2 3 4 5 6 7 8 9
| func testChannel() { c := make(chan func(int, int) int, 2)
c <- func(a int, b int) int { return a + b }
println((<- c)(1, 2)) }
|
3. 闭包(closure)
闭包:一个函数和与其相关的引用变量组成的一个实体
3.1 示例1
1 2 3 4 5 6 7 8 9 10
| func test(x int) func() { println(&x) return func() { println(&x, x) } }
func main() { test(5)() }
|
1
| go build -gcflags "-N -l" main.go
|
3.2 示例2
1 2 3 4 5 6 7 8 9 10 11 12 13
| func main() { f := closure(10) fmt.Println(f(1)) fmt.Println(f(2)) }
func closure(x int) func(int) int { fmt.Printf("%p\n", &x) return func(y int) int { fmt.Printf("%p\n", &x) return x + y } }
|
3.3 多匿名函数返回,延迟求值问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| func test() []func() { var fs []func()
for i := 0; i < 3; i++ { fs = append(fs, func() { println(&i, i) }) }
return fs }
func main() { for _, f := range test() { f() } }
|
修正后:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| func test() []func() { var fs []func()
for i := 0; i < 3; i++ { x := i fs = append(fs, func() { println(&x, x) }) }
return fs }
func main() { for _, f := range test() { f() } }
|
4. 递归函数
4.1 阶乘
1 2 3 4 5 6 7 8 9 10 11 12
| func factorial(n uint64) uint64 { if n > 0 { return n * factorial(n - 1) }
return 1 }
func main() { var i int = 15 fmt.Printf("%d 的阶乘等于 %d", i, factorial(uint64(i))) }
|
4.2 Fibonacci
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| func fibonacci(n uint64) uint64 { if n < 2 { return n }
return fibonacci(n-2) + fibonacci(n-1) }
func main() { for i := 0; i < 10; i++ { fmt.Printf("%d ", fibonacci(uint64(i))) } fmt.Println() }
|
5. 延迟调用(defer)
- FILO 先进后出
- 即使函数发生panic错误,也会执行
- 支持匿名函数调用
- 用于资源清理、文件关闭、解锁以及记录时间等操作
- 与匿名函数配合,可在return后修改函数的计算结果
5.1 示例1
1 2 3 4 5 6 7 8 9 10 11
| func main() { for i := 0; i < 3; i++ { defer fmt.Println(i) }
for i := 0; i < 3; i++ { defer func() { fmt.Println(i) }() } }
|
5.2 循环中使用延迟调用
延迟调用在函数结束时调用,如果将其放到循环中,会造成资源浪费
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| func main() { for i := 0; i < 1000; i++ { path := fmt.Sprintf("./log/%d.txt", i)
f, err := os.Open(path) if err != nil { log.Println(err) continue }
defer f.Close()
} }
|
优化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| func main() { do := func(i int) { path := fmt.Sprintf("./log/%d.txt", i)
f, err := os.Open(path) if err != nil { log.Println(err) return }
defer f.Close()
}
for i := 0; i < 1000; i++ { do(i) } }
|
5.3 延迟调用闭包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| func main() { var fs = [4]func(){}
for i := 0; i < 4; i++ { defer fmt.Println("defer i = ", i) defer func() { fmt.Println("defer_closure i = ", i) }() defer func(i int) { fmt.Println("defer_closure i = ", i) }(i) fs[i] = func() { fmt.Println("closure i = ", i) } }
for _, f := range fs { f() } }
|
5.4 延迟调用性能
相比直接用CALL汇编指令调用函数,延迟调用则须花费更大代价。这其中包括注册、调用等操作,还有额外的缓存开销。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| var m sync.Mutex
func call() { m.Lock() m.Unlock() }
func deferCall() { m.Lock() defer m.Unlock() }
func BenchmarkCall(b *testing.B) { for i := 0; i < b.N; i++ { call() } }
func BenchmarkDeferCall(b *testing.B) { for i := 0; i < b.N; i++ { deferCall() } }
|
5.5 for range
与闭包的坑
1 2 3 4 5 6 7 8 9 10 11
| func main() { s := []string{"a", "b", "c"}
for _, v := range s { go func() { fmt.Println(v) }() }
select {} }
|
6. 错误处理
标准库错误接口:
1 2 3
| type error interface { Error() string }
|
6.1 panic & recover
- panic: 主动抛出错误
- recover: 捕获panic抛出的错误
1 2
| func panic(v interface{}) func recover() interface{}
|
panic和recover运行机制:
1) 引发panic有两种情况:一是程序主动调用,二是程序产生运行时错误(Runtime Error),由运行时检测并退出
2) 发生panic后,程序会从调用panic的函数位置或发生panic的地方立即返回,逐层执行函数的的defer语句,然后逐层打印函数调用堆栈,直到recover捕获或运行到最外层函数
3) panic不但可以在函数正常流程中抛出,在defer逻辑里也可以再次调用panic或抛出panic。defer里面的panic能够被后续执行的defer捕获
4) recover用来捕获panic,阻止panic继续向上传递。recover()和defer一起使用,但是defer只有在后面的函数体内直接被调用才能捕获panic来终止,否则返回nil,异常继续向外传递。
注意:除非是不可恢复性、导致系统无法正常工作的错误,否则不建议使用panic。如:文件系统没操作权限、服务端口被占用、数据库未启动等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| func main() { result := div(8, 0) fmt.Println(result) }
func div(a int, b int) int { defer func() { err := recover() if err != nil { fmt.Println(err) } }()
return a / b }
|
6.2 主动panic并捕获
1 2 3 4 5 6 7 8 9 10
| func main() { defer func() { if err := recover(); err != nil { log.Fatalln(err) } }() panic("crash") println("exit.") }
|
6.3 无效捕获和有效捕获错误
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| defer recover() defer fmt.Println(recover()) defer func() { func() { recover() }() }()
defer func() { recover() }()
func except() { recover() }
func test() { defer except() panic("runtime error") }
|
6.4 多个panic,只会捕获最后一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| func main() { defer func() { if err := recover(); err != nil { fmt.Println(err) } }()
defer func() { panic("three") }()
defer func() { panic("two") }()
panic("one") }
|
7. 自定义错误
errors.New("错误描述")
:返回一个error类型的值,表示一个错误
panic
内置函数:接收一个interface{}类型的值(即任意值)作为参数,可以接受error类型的变量,输出错误信息,并退出程序。
7.1 负数平方根
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| func Sqrt(x float64) (float64, error) { if x < 0 { return 0, errors.New("math: square root of negative number") }
return math.Sqrt(x), nil }
func main() { result, err := Sqrt(-1)
if err != nil { fmt.Println(err) } else { fmt.Println(result) } }
|