C++中通过异常机制来处理一些异常的情况,这是C++错误处理的方式。
GO语言中,有专门的error类型来表示错误,这也是一种内置类型,它一般作为某些函数返回参数的第二个参数,来判断函数的调用是否出错,并可以将出错原因赋值给error类型的变量。
func main() {
f,err := os.Open("test.txt")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(f.Name(),"open successfully!")
}
执行结果
open test.txt: The system cannot find the file specified.
当我们试着去打开一个不存在的文件时,该函数其实返回的是两个值,一个是File类型,一个是error内置类型。
如果函数调用成功,也就是文件打开成功,期间没有出现任何的错误,那么返回的error就是nil,否则如果出错,返回的error就不是nil。
所以在这里也可以看到,error类型的默认零值为nil。
上述代码中我们将err和nil进行比较,这也是我们一贯的用法。因为只要函数调用没有出错,那么err的值一定是nil;只要函数调用出错了,那么err的值一定是一个不等于nil的值。
error内置类型内部组成
我们先来看一下上述open函数的底层模型
func Open(name string) (*File, error) {
return OpenFile(name, O_RDONLY, 0)
}
可以看到该函数的第二个返回值是一个error类型,再来看一下这个error类型的底层
type error interface {
Error() string
}
它其实就是一个接口,这个接口内部实现了一个方法Error,它的返回值是string,所以对于上面的代码, 我们还可以这么改。
func main() {
f,err := os.Open("test.txt")
if err != nil {
fmt.Println("Error:",err.Error())
return
}
fmt.Println(f.Name(),"open successfully!")
}
因为err是一个接口类型,所以我们也可以用接口的方式来调用,执行结果如下
Error: open test.txt: The system cannot find the file specified.
当然我们没必要这么麻烦,还是像之前那样直接打印err就可以了,编译器会自动帮我们找到err.Error()。
从错误获取更多信息
那么我们的error类型仅仅只有一个Error方法,我们能获取的只是出错原因,这样我们获取到的信息就太少了。有时候还想获取一些别的信息,怎么办?
其实open函数源码中的一行注释已经告诉我们方法了。
// If there is an error, it will be of type *PathError.
如果有错误,那么它将是类型*PathError。
这个PathError到底是什么?
// PathError records an error and the operation and file path that caused it.
type PathError struct {
Op string
Path string
Err error
}
func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }
这是一个结构体,它实现了Error方法,因此PathError类型实现了error接口。我们也可以直接调用。
通过这个结构体,我们就可以间接地获取到更多信息。先来看一下之前的出错原因
open test.txt: The system cannot find the file specified
对应着上面的PathError类型实现的Error方法,里面有return语句,我们可以发现,Op类型其实就是open,就是操作方式;Path类型其实是test.txt,就是文件路径;之后还有一个Err的error类型,打印的是The system cannot find the file specified。
因此,它实现的方式其实就是将三个变量组合起来组合成了一个完整的出错原因语句。如果我们想要得到完整语句的每一部分,也是可以的。
func main() {
f,err := os.Open("test.txt")
if err != nil {
if pathError,ok := err.(*os.PathError); ok != false {
fmt.Println(pathError.Op)
fmt.Println(pathError.Path)
fmt.Println(pathError.Err.Error())
}
return
}
fmt.Println(f.Name(),"open successfully!")
}
执行结果
open
test.txt
The system cannot find the file specified.
当然,这仅仅是一个PathError类型表示路径出错,还有别的出错类型,它们也是实现了Error方法,从而实现了error接口,来起到出错处理的目的,有些类型不仅实现了Error方法,还实现了别的一些方法来获取别的信息。
自定义错误类型
使用New函数可以自定义错误类型
func main() {
err := errors.New("I am an error")
if err != nil {
fmt.Println(err)
}
}
执行结果
I am an error