原文作者:学生黄哲
链接:https://www.jianshu.com/p/18dfd4772cdb
來源:简书
一、error
错误表示程序中出现了异常情况。Go 语言通过内置的错误接口提供了非常简单的错误处理机制。
• error类型是go语言的一种内置类型,使用的时候不用特定去import因为它本质上是一个接口
error类型是一个接口类型,这是它的定义:
1type error interface {2 Error() string3}interface {
2 Error() string
3}
(1)一个例子理解error
1package main 2import ( 3 "fmt" 4 "os" 5) 6func main() { 7 //试图打开一个并不存在的文件,这将会返回一个error 8 f, err := os.Open("/test.txt") 9 if err != nil {10 fmt.Println(err) //no such file or directory11 return12 }13 fmt.Println(f.Name(), "opened successfully")14}package main
2import (
3 "fmt"
4 "os"
5)
6func main() {
7 //试图打开一个并不存在的文件,这将会返回一个error
8 f, err := os.Open("/test.txt")
9 if err != nil {
10 fmt.Println(err) //no such file or directory
11 return
12 }
13 fmt.Println(f.Name(), "opened successfully")
14}
在go中处理错误的惯用方式是将返回的错误与nil进行比较。零值表示没有发生错误,而非零值表示存在错误。
(2)错误定制
上面也看到了error 有了一个签名为 Error() string 的方法。所有实现该接口的类型都可以当作一个错误类型。
第一、通过errors包去订制error
函数原型:func New(text string) error
使用字符串创建一个错误可以认为是New(fmt.Sprintf(...))。
1import "errors" //使用errors必须import "errors"包2error := errors.New("Myerror")3if error != nil {4 fmt.Print(err) //Myerror5}import "errors" //使用errors必须import "errors"包
2error := errors.New("Myerror")
3if error != nil {
4 fmt.Print(err) //Myerror
5}
demo
1package main 2import ( 3 "errors" 4 "fmt" 5 "math" 6) 7func circleArea(radius float64) (float64, error) { 8 if radius < 0 { 9 //使用字符串创建一个错误10 return 0, errors.New("Area calculation failed, radius is less than zero")11 }12 return math.Pi * radius * radius, nil13}14func main() {15 radius := -20.016 area, err := circleArea(radius)17 if err != nil {18 fmt.Println(err)19 return20 }21 fmt.Printf("Area of circle %0.2f", area)22}package main
2import (
3 "errors"
4 "fmt"
5 "math"
6)
7func circleArea(radius float64) (float64, error) {
8 if radius < 0 {
9 //使用字符串创建一个错误
10 return 0, errors.New("Area calculation failed, radius is less than zero")
11 }
12 return math.Pi * radius * radius, nil
13}
14func main() {
15 radius := -20.0
16 area, err := circleArea(radius)
17 if err != nil {
18 fmt.Println(err)
19 return
20 }
21 fmt.Printf("Area of circle %0.2f", area)
22}
第二种、通过fmt.Errorf()去订制
函数原型:func Errorf(format string, a ...interface{}) error
Errorf根据format参数生成格式化字符串并返回一个包含该字符串的错误。
1err := fmt.Errorf("error")2if err != nil {3 fmt.Print(err)4}err := fmt.Errorf("error")
2if err != nil {
3 fmt.Print(err)
4}
就不贴demo了
只需要把circleArea里if语句的返回值改为
1return 0, fmt.Errorf("Area calculation failed, radius %.2f is less than zero",radius)return 0, fmt.Errorf("Area calculation failed, radius %.2f is less than zero",radius)
第三种、使用结构体和字段来定制
1type MyError struct { 2err error 3} 4//订制Error() 5func (e MyError) Error() string { 6 return e.err.Error() 7} 8func main() { 9 err:=MyError{10 errors.New("error"),11 }12 fmt.Println(err.Error())13}type MyError struct {
2err error
3}
4//订制Error()
5func (e MyError) Error() string {
6 return e.err.Error()
7}
8func main() {
9 err:=MyError{
10 errors.New("error"),
11 }
12 fmt.Println(err.Error())
13}
demo
1package main 2import ( 3 "fmt" 4 "math" 5 6) 7type areaError struct { 8 err string 9 radius float6410}11func (e *areaError) Error() string {12 return fmt.Sprintf("radius %0.2f:%s", e.radius, e.err)13}1415func (e *areaError) IsRadiusNagative() bool {16 return e.radius < 01718}19func circleArea(radius float64) (float64, error) {20 if radius < 0 {21 return 0, &areaError{"Radius is negative", radius}22 }23 return math.Pi * radius * radius, nil24}25func main() {26 s, err := circleArea(-20)27 if err != nil {28 //将错误转换为具体的类型29 if err, ok := err.(*areaError); ok {30 fmt.Printf("Radius %.2f is less than zero", err.radius)31 return32 }33 fmt.Println(err)34 return35 }36 fmt.Println(s)37}package main
2import (
3 "fmt"
4 "math"
5
6)
7type areaError struct {
8 err string
9 radius float64
10}
11func (e *areaError) Error() string {
12 return fmt.Sprintf("radius %0.2f:%s", e.radius, e.err)
13}
14
15func (e *areaError) IsRadiusNagative() bool {
16 return e.radius < 0
17
18}
19func circleArea(radius float64) (float64, error) {
20 if radius < 0 {
21 return 0, &areaError{"Radius is negative", radius}
22 }
23 return math.Pi * radius * radius, nil
24}
25func main() {
26 s, err := circleArea(-20)
27 if err != nil {
28 //将错误转换为具体的类型
29 if err, ok := err.(*areaError); ok {
30 fmt.Printf("Radius %.2f is less than zero", err.radius)
31 return
32 }
33 fmt.Println(err)
34 return
35 }
36 fmt.Println(s)
37}
二、panic (抛出错误)和recover(捕获错误)
golang中没有try ... catch...这类异常捕获语句,但是提供了panic和recover内建函数,用于抛出异常以及异常的捕获。
• panic、 recover 参数类型为 interface{},因此可抛出任何类型对象。
• 如果程序出现了致命的错误,导致整个程序无法进行下去,golang提供了panic函数,用来实现程序的退出。
• 当程序发生 panic 时,使用 recover 可以重新获得对该程序的控
制。
• 不是所有的panic异常都来自运行时,直接调用内置的panic函数也会引发panic异常
• panic函数接受任何值作为参数。
(1)panic的使用
①延迟调⽤中引发的错误,可被后续延迟调⽤捕获,但仅最后⼀个错误可被捕获。
1func test() { 2defer func() { 3 fmt.Println(recover()) 4}() 5defer func() { 6 panic("defer panic") 7}() 8 panic("test panic") 9}10func main() {11 test() //defer panic12}func test() {
2defer func() {
3 fmt.Println(recover())
4}()
5defer func() {
6 panic("defer panic")
7}()
8 panic("test panic")
9}
10func main() {
11 test() //defer panic
12}
②当函数发生 panic 时,它会终止运行,在执行完所有的延迟函数后,程序控制返回到该函数的调用方。这样的过程会一直持续下去,直到当前协程的所有函数都返回退出,然后程序会打印出 panic 信息,接着打印出堆栈跟踪,最后程序终止。
如果函数没有 panic,调用 recover 函数不会获取到任何信息,也不会影响当前进程。
demo
1package main 2import ( 3 "fmt" 4) 5func fullName(firstName *string, lastName *string) { 6 if firstName == nil { 7 panic("Firsr Name can't be null") 8 } 9 if lastName == nil {10 panic("Last Name can't be null")11 }12 fmt.Printf("%s %s\n", *firstName, *lastName)13 fmt.Println("returned normally from fullName")14}15func test(){16 defer fmt.Println("deferred call in test")17 firName := "paul"18 fullName(&firName, nil)19}20func main() {21 defer fmt.Println("deferred call in main")22 test()23 fmt.Println("returned normally from main")24}package main
2import (
3 "fmt"
4)
5func fullName(firstName *string, lastName *string) {
6 if firstName == nil {
7 panic("Firsr Name can't be null")
8 }
9 if lastName == nil {
10 panic("Last Name can't be null")
11 }
12 fmt.Printf("%s %s\n", *firstName, *lastName)
13 fmt.Println("returned normally from fullName")
14}
15func test(){
16 defer fmt.Println("deferred call in test")
17 firName := "paul"
18 fullName(&firName, nil)
19}
20func main() {
21 defer fmt.Println("deferred call in main")
22 test()
23 fmt.Println("returned normally from main")
24}
输出
(2)recover的使用
如果 goroutine 没有 panic,那调用 recover 函数会返回 nil。
捕获函数 recover 只有在延迟调⽤内直接调⽤才会终⽌错误,否则总是返回 nil。任何未捕获的错误都会沿调⽤堆栈向外传递。
修改一下上面的例子使用recover来捕获异常
1package main 2import ( 3 "fmt" 4) 5func recoverName() { 6 if r := recover(); r != nil{ 7 fmt.Println("recovered from ",r) 8 } 9}10func fullName(firstName *string, lastName *string) {11 defer recoverName()12 if firstName == nil {13 panic("Firsr Name can't be null")14 }15 if lastName == nil {16 panic("Last Name can't be null")17 }18 fmt.Printf("%s %s\n", *firstName, *lastName)19 fmt.Println("returned normally from fullName")20}21func test(){22 defer fmt.Println("deferred call in test")23 firName := "paul"24 fullName(&firName, nil)25}26func main() {27 defer fmt.Println("deferred call in main")28 test()29 fmt.Println("returned normally from main")30}package main
2import (
3 "fmt"
4)
5func recoverName() {
6 if r := recover(); r != nil{
7 fmt.Println("recovered from ",r)
8 }
9}
10func fullName(firstName *string, lastName *string) {
11 defer recoverName()
12 if firstName == nil {
13 panic("Firsr Name can't be null")
14 }
15 if lastName == nil {
16 panic("Last Name can't be null")
17 }
18 fmt.Printf("%s %s\n", *firstName, *lastName)
19 fmt.Println("returned normally from fullName")
20}
21func test(){
22 defer fmt.Println("deferred call in test")
23 firName := "paul"
24 fullName(&firName,
- 文章目录
- 繁