4.6. 处理运行时错误¶
Go语言的错误处理私信及设计包含一些特征:
一个可能造成错误的函数,需要返回值中返回一个错误接口(error).如果调用是成功的。错误接口将返回nil,否则返回错误。
在函数调用后需要检查错误,如果发生错误,进行必要的错误处理。
Go语言希望开发者将错误处理视为正常开发必须实现的环境,正确地处理每一个可能发生错误的函数。 同时,Go语言使用返回值返回错误的机制,也能大幅度降低编译器、运行时处理错误的复杂度,让开发者真正掌握错误的处理。
4.6.1. 1.net包中的例子¶
net.Dial()是Go语言系统包net即中的一个函数,一般用于创建Socket连接。
net.Dial()拥有两个返回值,即Conn和error,这个函数是阻塞的,因此在Socket操作后,会返回Conn连接对象和error;如果发生错误,error会告知错误的类型,Conn会返回为空。
根据Go语言的错误处理机制,Conn是其重要的返回值,因此为这个函数增加了一个错误返回, 类似为error,参见如下代码:
func Dial(network, address string) (Conn, error) {
var d Dialer
return d.Dial(network, address)
}
在io包中的Writer接口也拥有错误返回,代码如下:
type Writer interface {
Write(p []byte) (n int, err error)
}
io包中还有一个Closer接口,只有一个错误返回,代码如下:
type Closer interface {
Close() error
}
4.6.2. 2.错误接口的定义格式¶
error是Go系统声明的接口类型,代码如下:
type error interface {
Error() string
}
所有符合Error()string格式的方法,都能实现错误接口。 Error()返回错误的具体描述,使用者可以通过这个字符串知道发生了什么错误。
4.6.3. 3.自定义一个错误¶
返回错误前,需要定义会生成哪些可能的错误。在Go语言中,使用errors包进行错误的定义,格式如下:
var err = errors.New("this is an error")
错误字符串由于相对固定,一般在包作用域声明,应尽量减少在使用时直接使用errors.New返回。
3.1 errors包¶
Go语言的errors中对New的定义非常简单,代码如下:
// 创建错误对象
// 将errorString结构体实例化,并赋值错误描述的成员。
func New(text string) error {
return &errorString{text}
}
// 错误字符串
// 声明errorString结构体,拥有一个成员,描述错误内容
type errorString struct {
s string
}
// 返回发生任何错误
// 实现error接口的Error()方法,该方法返回成员中的错误描述
func (e *errorString) Error() string {
return e.s
}
3.2 在代码中使用错误定义¶
package main
import (
"errors"
"fmt"
)
// 定义除数为0的错误
var errDivisionByZero = errors.New("division by zero")
// 除法函数,定义传入2个数都为int,返回一个int或error
func div(dividend, divisor int) (int, error) {
// 判断除数为0的情况并返回
if divisor == 0 {
return 0, errDivisionByZero
}
// 返回计算后的值,error为nill
return dividend / divisor, nil
}
func main() {
fmt.Println(div(1, 0)) //0 division by zero
}
3.3 示例:在解析中使用自定义错误¶
使用errors.New定义的错误字符串的错误类型是无法提供丰富的错误信息的。那么需要携带错误信息返回, 就需要借助自定义结构体实现错误接口。
package main
import "fmt"
// 声明一个解析错误的结构体,解析错误包含2个成员,filename和line
type ParseError struct {
Filename string // 文件名
Line int // 行号
}
// 实现error接口,返回错误描述,格式化成员的文件名和行号并返回
func (e *ParseError) Error() string {
return fmt.Sprintf("%s %d", e.Filename, e.Line)
}
// 创建一些解析错误
func newParseError(filename string, line int) error {
return &ParseError{filename, line}
}
func main() {
// 声明一个错误接口类型
var e error
// 创建错误实例,包含文件名和行号
e = newParseError("main.go", 1)
// 通过error接口插件错误描述
fmt.Println(e.Error()) //main.go 1
// 根据错误接口的具体类型,获取详细的错误信息
switch detail := e.(type) {
case *ParseError: // 这是一个解析错误
fmt.Printf("Filename: %s Line: %d\n", detail.Filename, detail.Line) //Filename: main.go Line: 1
default:
fmt.Println("other error")
}
}