Go的异常处理机制

在实际的项目中,对于异常的最佳实践不少,在使用不一样的语言开发不一样类型的程序时有不一样的建议。Go语言中没有使用try...catch相似的异常处理机制,而是提供了panicrecover函数来处理所谓的运行时异常,也就是所谓的错误处理机制。配合defer语句和error接口开发者能够很是灵活地处理运行时的错误和异常。bash

代码中随便使用panic显然不是一个好方式,咱们知道在风险控制中,有所谓已知的未知未知的未知,在Go程序设计中,对于前者咱们能够经过预先开发的代码分支来处理,对于后者就比较棘手了:ide

  1. 若是项目中的代码、使用的标准库以及第三方库在运行时内部捕获了异常并经过合适的error对象返回给调用者,那咱们能够尽可能少甚至能够不用panic函数。
  2. 若是没法保证上面的状况,那为了确保程序在运行时不会由于未知的未知致使崩溃,那panic函数可能不得不加在任何须要的地方。

咱们应该认识到,panic函数是咱们和计算机都不但愿看到的,应该在设计开发的时候充分考虑使用场景可能出现的状况,处理好已知的未知,在肯定须要的地方使用panic机制。函数

defer

defer关键字用来标记最后执行的Go语句,通常用在资源释放、关闭链接等操做,会在函数关闭前调用。多个defer的定义与执行相似于栈的操做:先进后出,最早定义的最后执行。ui

package main

import (
    "fmt"
)

func testA() int {
    x := 5
    defer func() {
    	x++
    	fmt.Printf("testA defer func-1[x=>%d]\n", x)
    }()
    defer func() {
    	x++
    	fmt.Printf("testA defer funn-2[x=>%d]\n", x)
    }()
    return x
}

func testB() (x int) {
    x = 5
    defer func() {
    	fmt.Println("testB defer func")
    }()
    x++
    return
}

func main() {
    a := testA()
    b := testB()
    fmt.Printf("testA return %d\n", a)
    fmt.Printf("testB return %d\n", b)
}
复制代码

执行后输出结果:编码

testA defer funn-2[x=>6]
testA defer func-1[x=>7]
testB defer func
testA return 5
testB return 6
复制代码

分析如上代码,return xxx语句并非一条原子指令,在其执行的时候语句会分解成返回变量=xxxreturn,最后执行return。以前说defer语句是在函数关闭的时候调用,确切的说是在执行return语句的时候调用,注意是return不是return xxx。在同一个函数里的defer会按照栈的先进后出原则执行。spa

Error

Go语言经过支持多返回值,让在运行时返回详细的错误信息给调用者变得很是方便。咱们能够在编码中经过实现error接口类型来生成错误信息,error接口的定义以下:设计

type error interface {
    Error() string
}
复制代码

经过下面的例子来看一下error接口的使用:code

package main

import (
    "fmt"
)

type error interface {
    Error() string
}

// DivisionError 类型定义
type DivisionError struct {
    dividend int
    divider  int
}

// Error 方法实现
func (de *DivisionError) Error() string {
    strFormat := ` Cannot proceed, the divider is zero. dividend: %d divider: 0`
    return fmt.Sprintf(strFormat, de.dividend)
}

// Divide 函数定义
func Divide(varDividend int, varDivider int) (result int, errorMsg string) {
    if varDivider == 0 {
    	dData := DivisionError{
            dividend: varDividend,
            divider:  varDivider,
    	}
    	errorMsg = dData.Error()
    } else {
    	result = varDividend / varDivider
    }
    return
}

func main() {
    result, err := Divide(100, 10)
    if err == "" {
    	fmt.Printf("100/10=%d\n", result)
    }
    _, err = Divide(100, 0)
    if err != "" {
    	fmt.Printf("errorMsg is: %s\n", err)
    }
}
复制代码

运行后能够看到以下输出:orm

100/10=10
errorMsg is:
        Cannot proceed, the divider is zero.
        dividend: 100
        divider: 0
复制代码

panic和recover

panic和recover是两个内置函数,panic函数用于抛出异常,recover函数用于捕获panic函数的参数信息。recover函数只有在defer语句调用的函数中直接调用时才能生效,若是goroutine没有panic,那recover函数会返回nil。对象

package main

import (
    "fmt"
)

// SimplePanicRecover panic/recover简单的例子
func SimplePanicRecover() {
    defer func() {
    	if err := recover(); err != nil {
    	    fmt.Println("Panic info is:", err)
    	}
    }()
    panic("SimplePanicRecover panic!")
}

// MultiPanicRecover 多个panic/recover
// 当defer中也调用了panic函数时,最后被调用的panic函数的参数会被后面的recover函数获取到
// 一个函数中能够定义多个defer函数,按照FILO的规则执行
func MultiPanicRecover() {
    defer func() {
    	if err := recover(); err != nil {
    	    fmt.Println("Panic info is:", err)
    	}
    }()
    defer func() {
    	panic("MultiPanicRecover defer inner panic!")
    }()
    defer func() {
    	if err := recover(); err != nil {
    	    fmt.Println("Panic info is:", err)
    	}
    }()
    panic("MultiPanicRecover panic!")
}

// NoPanicButHasRecover 只有panic没有recover
// 若是函数没有panic函数,调用recover函数不会获取到任何信息,也不会影响当前进程
func NoPanicButHasRecover() {
    if err := recover(); err != nil {
    	fmt.Println("NoPanicButHasRecover Panic info is:", err)
    } else {
    	fmt.Println("NoPanicButHasRecover Panic info is:", err)
    }
}

func main() {
    SimplePanicRecover()
    MultiPanicRecover()
    NoPanicButHasRecover()
}
复制代码

运行后能够看到以下输出:

Panic info is: SimplePanicRecover panic!
Panic info is: MultiPanicRecover panic!
Panic info is: MultiPanicRecover defer inner panic!
NoPanicButHasRecover Panic info is: <nil>
复制代码
相关文章
相关标签/搜索