目录

本文介绍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捕获的错误处理代码能够执行。

您可能感兴趣的文章: