1 error 接口
Go 语言通过 error 接口实现错误处理的标准模式,通过使用函数返回值列表中的最后一个值返回错误信息,将错误的处理交由程序员主动进行处理
在程序中运行时错误错误一般分为两种:
-
可恢复的错误(重试/或忽略)
-
不可恢复的错误(程序退出)
范例代码:
package main
import (
"fmt"
"strconv"
)
func main() {
// 需要给调用者返回错误信息
// 通过函数最后一个返回值返回错误!
// 调用者需要对错误进行检查,决定如何操作
v, err := strconv.Atoi("123")
if err != nil {
fmt.Println(err)
} else {
fmt.Println(v)
}
}
1.2 error 接口初始化方法
下面两种效果其实是一样的,并没有区别
1.2.1 第一种初始化 error 方式
通过 fmt.Errorf 方法创建
定义函数实现 error 范例:
package main
import (
"fmt"
)
// error 接口类型
func div(left, right int) (int, error) {
if right == 0 {
// 在 go 中通过, fmt.Errorf 就看返回错误
return 0, fmt.Errorf("right num is zeror")
}
return left / right, nil
}
func main() {
res, err := div(10, 0)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(res)
}
}
1.2.2 第二种初始化 error 方法
通过 errors 包的 New 方法创建
范例代码:
package main
import (
"errors"
"fmt"
)
// error 接口类型
func div(left, right int) (int, error) {
if right == 0 {
// 在 go 中通过, errors.New 即可返回错误
return 0, errors.New("传递参数错误")
}
return left / right, nil
}
func main() {
res, err := div(10, 0)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(res)
}
}
1.3 对 error 错误进行处理
看下面代码,如果说传递个 div 的参数是错误的话,我们可以进行调试
package main
import (
"errors"
"fmt"
)
// error 接口类型
func div(left, right int) (int, error) {
if right == 0 {
return 0, errors.New("right num is 0")
}
return left / right, nil
}
func main() {
if res, err := div(10, 0); err != nil {
fmt.Println("value is error :", err)
} else {
fmt.Println(res)
}
}
2 defer 延迟执行-压栈
defer 关键字用户声明函数,不论函数是否发生错误都在函数执行最后执行(return 之前),若使用 defer 声明多个函数,则按照声明的顺序,先声明后执行(堆)常用来做资源释放,记录日志等工作
我们可以看到通过下面代码,defer 的执行顺序是最后执行,也就是说当函数存在其他代码块的时候,优先执行其他代码块,最后在执行 defer ,而且当有多个 defer 的时候是先进后出
package main
import "fmt"
func main() {
fmt.Println("start")
defer func() {
fmt.Println("defer")
}()
defer func() {
fmt.Println("12")
}()
fmt.Println("END")
}
通过输出,我们会还想 fmt.Println("defer")
是先进,但是后出
所以 defer 的执行顺序是先进后出,谁先声明谁后执行
2.1 defer 细节1
我们可以通过下面代码发现, defer 并不受循环影响。
package main
import "fmt"
func main() {
for i := 0; i < 2; i++ {
fmt.Println("start")
defer func() {
// 这里的 i 寻找的是 for 循环的 i
fmt.Printf("defer %d\n", i)
}()
fmt.Println("stop")
}
}
输出结果看到,defer 2 ,为什么会是 2 呢,因为 defer 在使用的时候其实使用了外部的变量,所以最后 i 都成 2 了,由于 for 循环完毕执行了 i++
可以通过下面代码解决上面问题
每次执行完了之后我们将 i 传递到 defer func()
中去
package main
import "fmt"
func main() {
for i := 0; i < 2; i++ {
fmt.Println("start")
defer func(i int) {
fmt.Printf("defer %d\n", i)
}(i)
fmt.Println("stop")
}
}
延迟执行或者闭包的时候,参数传递的时候一定要注意
2.2 defer 细节2
在延迟执行中尽量不要修改返回值
package main
import "fmt"
func test() (i int) {
// 在延迟执行中尽量不要修改返回值
i = 1
defer func() {
fmt.Println("defer")
// 在 defer 中将 i 修改为 2
i = 2
}()
return i
}
func main() {
fmt.Println(test())
}
3 panic & recover
go 语言提供 panic 和 recover 函数用于处理运行时错误,当调用 panic 抛出错误,中断原有的控制流程,常用于不可修复性错误。recover 函数用于终止错误处理流程,仅在 defer语句的函数中有效,用于截取错误处理流程,recover 只能捕获到最后一个错误
处识 panic
panic 其实是在程序员执行程序的时候用来抛出错误
package main
func main() {
panic("我是错误")
}
当出现错误,一般要用延迟执行加 recover
package main
import "fmt"
func main() {
defer func() {
if err := recover(); err != nil {
fmt.Println("出错了,进行恢复", err)
}
}()
panic("我是错误")
}
假如我们把 panic 注释掉就不抛出错误,因为 recover 只能够捕获 panic
package main
import "fmt"
func main() {
defer func() {
if err := recover(); err != nil {
fmt.Println("出错了,进行恢复", err)
}
}()
// panic("我是错误")
}
通过函数调用实现控制 recover 和 panic
package main
import "fmt"
func test(p bool) {
defer func() {
if err := recover(); err != nil {
fmt.Println("出现错误:", err)
}
}()
if p {
panic("panic error")
}
fmt.Println("running...")
}
func main() {
test(true)
test(false)
}
recover 还能够捕获其他函数中的 painc
package main
import "fmt"
func callback(p bool) {
if p {
panic("callback panic")
}
fmt.Println("running...")
}
func test(p bool) {
defer func() {
if err := recover(); err != nil {
fmt.Println("出错:", err)
}
}()
// 传入 callback 函数调用 p
callback(p)
}
func main() {
test(true)
test(false)
}
通过输出发现 recover 也能捕获到其他函数的错误
3.1 recover 的处理
recover 的处理,一般会用 recover 捕获到错误,然后进行返回
package main
import "fmt"
func callback(p bool) {
if p {
panic("callback panic") // p 为 true 返回 panic
}
fmt.Println("running...")
}
// 定义返回值为 error 类型
func test(p bool) (err error) {
defer func() {
if msg := recover(); msg != nil { // 定义 msg 接收 recover 的返回值,捕获panic
err = fmt.Errorf("%s", msg)
}
}()
callback(p)
// 返回命名返回值
return
}
func main() {
fmt.Println(test(true))
fmt.Println(test(false))
fmt.Println(test(false))
}