error
官方推荐的标准做法是返回error状态。
func Scanln(a ...interface{}) (n int, err error)
标准库将error定义为接口类型,以便实现自定义错误类型。
type error interface {
Error() string
}
按惯例,error总是最后一个返回参数。标准库提供了相关创建函数,可方便地创建包含简单错误文本的error对象。
var errDivByZero = errors.New("division by zero")
func div(x ,y int) (int, error){
if y == 0 {
return 0, errDivByZero
}
return x /y, nil
}
func main() {
z, err := div(5,0)
if err == errDivByZero {
log.Fatal(err) //打印输出内容,退出应用程序,defer函数不会执行
}
println(z)
}
输出:
2019/08/01 16:27:50 division by zero
某些业务场景,我们想自定义错误类型,怎么做呢?
type DivError struct { //自定义错误类型
x ,y int
}
func (DivError) Error() string { //实现error接口方法
return "division by zero"
}
func div(x ,y int) (int, error) {
if y == 0 {
return 0, DivError{x, y} //返回自定义错误类型
}
return x/y, nil
}
func main() {
z, err := div(5,0)
if err != nil {
switch e := err.(type) { //根据类型匹配
case DivError:
fmt.Println(e, e.x, e.y)
default:
fmt.Println(e)
}
log.Fatal(err)
}
println(z)
}
输出:
division by zero 5 0
2019/08/01 16:40:06 division by zero
panic,recover
与error相比,panic/recover在使用方法上更接近try/catch结构化异常。
func panic(v interfaceP{})
func recover() interface{}
它们是内置函数而非语句。panic会立即中断当前函数流程,执行延迟调用。而在延迟调用中,recover可捕获并返回panic提交的错误对象。
func main() {
defer func() {
if err := recover(); err != nil { //错误捕获
log.Fatal(err)
}
}()
panic("i am dead") //引发错误
println("exit.") //永不会执行
}
输出:
2019/08/01 16:59:17 i am dead
无论是否执行recover,所有延迟调用都会被执行。但中断性错误会沿调用堆栈向外传递,要么被外层捕获,要么导致进程崩溃。
连续调用panic,仅最后一个会被recover捕获
func main() {
defer func() {
for {
if err := recover(); err != nil {
log.Println(err)
}else {
log.Fatal("fatal")
}
}
}()
defer func() { //类似重新抛出异常
panic("you are dead") //可先recover捕获,包装后重新抛出
}()
panic("i am dead")
}
输出:
2019/08/01 17:52:11 you are dead
2019/08/01 17:52:11 fatal
建议:除非是不可恢复性、导致系统无法正常工作的错误,否则不建议使用panic