学习 golang 的过程中,会在很多情况下接触 error,实际上是 interface 类型,下面是它的定义:
type error interface {
Error() string
}
Go 语言的接口设计是非侵入式的,只要实现对应 interface 的方法就可以,所以只要实现 Error() 方法的类型都属于 error 接口类型。
创建 error 最简单的方法就是调用 errors.New 函数,其次还有一个方便的封装函数 fmt.Errorf(),会处理字符串格式化。系统中还有很多 error 的实现类型:PathError,syscall.Errno。
func main(){
err := errors.New("create a error")
fmt.Println("err: ", err) //err: create a error
fmt.Println("Error():", err.Error()) //Error(): create a error
}
注意下上面Println()的两种入参:打印结果是一样的。
事实上go有一个类似于java的 toString 方法,不过在go中它是一个接口,定义如下,只要实现了 String() 方法,调用 print 系列函数的时候就会执行。
type Stringer interface {
String() string
}
但是 error 接口并没有实现 String() 方法,但是在打印的时候却可以直接输出,查看源码(src/fmt/print.go)发现,内部这里做了特殊处理,会利用反射机制+类型选择,自动匹配到error接口类型,然后调用Error()方法。
switch v := p.arg.(type) {
case error:
handled = true
defer p.catchPanic(p.arg, verb, "Error")
p.fmtString(v.Error(), verb) //这里调用Error()方法
return
case Stringer:
handled = true
defer p.catchPanic(p.arg, verb, "String")
p.fmtString(v.String(), verb) //这里调用String()方法
return
}
上面除了调用 Error() 方法,根据类型选择也会调用 String() 方法,至此也就明白了,为什么可以直接打印error类型,同时所有实现Stringer接口的类型也可以直接调用print系列函数。
之前看的一篇文章 Go-Errors 也提到了这个情况,但是在 error 这一块并没有解释很清楚透彻。还有一篇文章 Be careful about printing error as string in GoLang
中用自定义的A类型实现了 Error() 方法然后打印报错了,因为入参的类型是 A 而不是 error,有兴趣可以试下,代码如下:
package main
import "fmt"
type A string
func (a A) Error() string {
return fmt.Sprintf("%s is an error", a)
}
func main() {
a := A("hello")
fmt.Printf("error is %s", a)
}