GoLang panic 用法

文章目录

  • 一、Go panic用法
  • 二、recover:
  • 三、偶尔的 panic 是必要的
  • Go 中 panic 恢复的限制条件


一、Go panic用法

Go语言追求简洁优雅,所以,Go语言不支持传统的 try…catch…finally 这种异常,因为Go语言的设计者们认为,将异常与控制结构混在一起会很容易使得代码变得混乱。因为开发者很容易滥用异常,甚至一个小小的错误都抛出一个异常。在Go语言中,使用多值返回来返回错误。不要用异常代替错误,更不要用来控制流程。在极个别的情况下,也就是说,遇到真正的异常的情况下(比如除数为 0了)。才使用Go中引入的Exception处理:defer, panic, recover。

这几个异常的使用场景可以这么简单描述:Go中可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理。

package main

import "fmt"

func main(){
    fmt.Println("c")
     defer func(){ // 必须要先声明defer,否则不能捕获到panic异常
        fmt.Println("d")
        if err:=recover();err!=nil{
            fmt.Println(err) // 这里的err其实就是panic传入的内容,55
        }
        fmt.Println("e")
    }()

    f() //开始调用f
    fmt.Println("f") //这里开始下面代码不会再执行
}

func f(){
    fmt.Println("a")
    panic("异常信息")
    fmt.Println("b") //这里开始下面代码不会再执行
    fmt.Println("f")
}


输出结果:

c
a
d
异常信息
e
  • 内建函数
  • 假如函数F中书写了panic语句,会终止其后要执行的代码,在panic所在函数F内如果存在要执行的defer函数列表,按照defer的逆序执行
比如panic函数内有:
    defer   函数1
    defer   函数2
    defer   函数3
那么执行顺序就是:
    函数3
    函数2
    函数1
  • 返回函数F的调用者G,在G中,调用函数F语句之后的代码不会执行,假如函数G中存在要执行的defer函数列表,按照defer的逆序执行,这里的defer 有点类似 try-catch-finally 中的 finally
  • 到goroutine整个退出,并报告错误

二、recover:

  • 内建函数
  • 用来控制一个goroutine的panicking行为,捕获panic,从而影响应用的行为
  • 一般的调用建议(如上面的例子)
    a). 在defer函数中,通过recever来终止一个gojroutine的panicking过程,从而恢复正常代码的执行
    b). 可以获取通过panic传递的error
    简单来讲:go中可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理。

注意:利用recover处理panic指令,defer必须在panic之前声明,否则当panic时,recover无法捕获到panic.

三、偶尔的 panic 是必要的

Go 是一种安全的语言,运行时检查一些严重的编程错误。例如在你访问超出 slice 边界的元素时,这种行为是未定义的,因此 Go 会在运行时 panic。例如下面的小程序。

package main

import (
    "fmt"
)

func main() {
    s := make([]string, 3)
    fmt.Println(s[5])
}

程序将终止于一个运行时 error。

panic: runtime error: index out of range

goroutine 1 [running]:
main.main()
    /tmp/sandbox209906601/main.go:9 +0x40

其他一些会引发 panic 的就是通过值为 nil 的指针访问结构体的字段,关闭已经关闭的 channel 等。怎样选择性的 panic ?可以通过访问 slice 时返回 result,error 两个值的方式实现。也可以将 slice 的元素赋值给一个可能返回 error 的函数,但是这样会将代码变复杂。想象一下,写一个小片段,foo,bar,baz 都只是一个字符串的一个 slice,实现片段之间的拼接。

foo[i] = bar[i] + baz[i]

就会变成下面这样冗长的代码:

br, err := bar[i]
if err != nil {
    return err
}
bz, err := baz[i]
if err != nil {
    return err
}
err := assign_slice_element(foo, i, br + bz)
if err != nil {
    return err
 }

这不是开玩笑,不同语言处理这样的方式是不一样的。如果 slices/lists/arrays 的指针 i 越界了,在 Python 和 Java 中就会抛出异常。C 中没有越界检查,所以你就可以尽情的蹂躏边界外的内存空间,最后将导致程序崩溃或者暴露安全漏洞。C++ 中将采用折中的处理方式。性能优先的模块采用这种不安全的 C 模式,其他模块(比如 std::vector::at)采用抛出异常的方式。
因为上面重写的小片段变得如此冗长是不可接受的。Go 选择了 panic ,这是一种类似异常的机制,在代码中保留了像 bugs 这样最原始的异常条件。

这不只是内建代码能够这样用,自定义代码也可以在任何需要的地方调用 panic。在有些可能导致可怕错误的地方还鼓励使用 panic 抛出 error,比如 bug 或者一些关键因素被违反的时候。比如在 swich 的某个 case 在当前上下文中是不可能发生的,在这种 case 中只有一个 panic 函数。这无形中等价于 Python 中的 raise 或者 C++ 中的 throw。这也强有力的证明了在捕获异常方面 Go 的异常处理的特殊之处。

Go 中 panic 恢复的限制条件

一般就HTTP服务的全局做一个recover,然后任何地方panic都能全局捕获相应一个错误
GoLang panic 用法_第1张图片

你可能感兴趣的:(go,golang,开发语言,后端)