目录
本文介绍Golang错误处理机制,包括不同类型错误处理、定义运行时错误等内容。
golang错误处理机制
Go错误处理类似C语言,没有提供任何异常,以及类java语言使用的try/catch异常处理机制。go异常处理仅简化为预定义的Error类型,Go没有提供异常处理机制,不能抛出类似许多其他语言的异常。相反,Golang集成了新的错误处理机制,如panic 和 recovery。
error类型
error类型仅包括Error方法,返回string类型标识具体的错误信息。代码如下:
// The built-in error interface is a regular interface for handling errors. // where nil means no errors. type error interface { Error() string }
我们看到错误处理类型实际上是一个接口,包含一个简单的error()方法,其返回值是一个字符串。通过定义可知:要实现错误处理,只需要向error()方法返回一个简单的字符串。
示例
下面先看一个错误处理示例:
func main() { conent, err := openFile() if err != nil { fmt.Println(err) } else { fmt.Println(string(conent)) } }
err!= nil
strconv.Atoi()
下面示例检查用户输入数据是否为数值,通过示例可以学习如何处理错误:
package main import ( "fmt" "strconv" ) func main() { var input string fmt.Print("Type some number: ") fmt.Scanln(&input) number, err := strconv.Atoi(input) if err == nil { fmt.Println(number, "is number") } else { fmt.Println(input, "is not number") fmt.Println(err.Error()) } }
如果有错误显示错误信息,反之输出结果。
创建错误对象
内置方法
除了内置函数返回错误,我们可以创建错误,主要有两种方法:
- errors.New()
- fmt.Errorf()
下面示例展示如何自定义错误对象。首先提供一个validate()对象,之后用于检查用户数是否为空,如果为空产生错误:
func validate(input string) (bool, error) { if strings.TrimSpace(input) == "" { return false, fmt.Errorf("%s can't be empty", input) //return false, errors.New("cannot be empty") } return true, nil } func main() { var name string fmt.Print("Type your name: \n") fmt.Scanln(&name) if valid, err := validate(name); valid { fmt.Println("hello", name) } else { fmt.Println(err.Error()) } }
第三方库
有时需要在错误对象上增加额外信息:
import "github.com/pkg/errors" internal := errors.New("internal error") // 给error增加其他上下文信息 wrapped := errors.Wrap(internal, "wrapper") // 获得原始错误信息 unwrapped := errors.Cause(wrapped)
pkg/errors是内置errors的替代工具,尽管内置error也提供了类似功能:
import "errors" internal := errors.New("internal error") // add additional context to an error wrapped := fmt.Errorf("wrapper: %w", internal) // get original error unwrapped := errors.Unwrap(wrapped)
但内置功能相对较少,建议直接使用pkg/errors.
完整示例:
import ( "fmt" "github.com/pkg/errors" "strings" ) func validate(input string) (bool, error) { if strings.TrimSpace(input) == "" { err := errors.New("can't be empty") return false, errors.Wrap(err, "input error") } return true, nil } func main() { var name string fmt.Print("Type your name: \n") fmt.Scanln(&name) if valid, err := validate(name); valid { fmt.Println("hello", name) } else { fmt.Println(errors.Unwrap(err)) } }
panic
panic是Go内置函数,类似其他语言的异常。当运行时出现该函数,则程序在该点终止。
声明panic
go代码中遇到panic时,不再继续执行。这种这种场景可以使用内置panic函数:
func panic( interface{} )
可以传入字符串或其他类型参数,情况示例:
package main import "fmt" func main() { fmt.Println("start Go program") panic(" built in panic keyword gives error msg") // panic keyword fmt.Println("End Go program") }
内置操作
package main import ( "fmt" ) func main() { a,b := 1,0 // variable a & b are declared and initialized result := a/b // division operation fmt.Println(result) }
上面代码因为除数为0,程序终止并报错:panic: runtime error: integer divide by zero
另外方法slice时,索引超出范围时也会panic:
package main import "fmt" func main() { names := []string{ //slice data type "Learn eTutorials", // index 0 "Golang", //index 1 "panic tutorial", //index 2 } //fmt.Println(names[0]) commented //fmt.Println(names[2]) commented fmt.Println(names[4]) //panic: runtime error: index out of range [4] with length 3 }
那么时间使用panic函数呢?
- 当程序不能继续执行时使用。举例,web服务器不能绑定特定端口。
- 程序出错时使用。举例,假设方法接收指针类型参数,实际调用时传入nil。
panic与defer
还是从示例开始吧:
func Name(firstName *string, lastName *string) { defer fmt.Println("Name function deferred call") if firstName == nil { panic("runtime error: first name cannot be nil") } if lastName == nil { panic("runtime error: last name cannot be nil") } fmt.Printf("%s %s\n", *firstName, *lastName) fmt.Println("return from Name") } func main() { defer fmt.Println("main() deffered call") firstName := "Elon" Name(&firstName, nil) fmt.Println("return from main") }
输出结果为:
Name function deferred call
main() deffered call
panic: runtime error: last name cannot be nil
我们看到多个defer,类似栈,先进后出。panic总是让所有defer执行完毕后才抛出。
recover
前面我们看了error和panic两类异常,前者类似java中的运行时异常,后者类似于非运行时异常。error我们可以捕获或忽略,但panic要么终止运行,要么手动恢复,也就是手动处理这类错误,如给用户提示。下面同时示例来说明:
func main() { fmt.Println("lets learn about recover() in golang") Panicfunc() fmt.Println("learned all about recover() ") } func Panicfunc() { defer Panicrecover() fmt.Println("instruction just before panicking situation") panic("Panicfunc resume execution") fmt.Println("instruction after panic does not execute") } func Panicrecover() { if err := recover(); err != nil { fmt.Printf("Recovered from panic: %v \n", err) } }
输出结果:
lets learn about recover() in golang
instruction just before panicking situation
Recovered from panic: Panicfunc resume execution
learned all about recover()
我们看到panic之后代码不会执行,但recover捕获的错误处理代码能够执行。