为何Go语言中打印error变量e输出<nil>而非实际类型?
这问题我刚接触Go的时候也琢磨过半天!咱们先从fmt.Println的返回值说起——它的签名是func Println(a ...interface{}) (n int, err error),这里的err是error接口类型,不是某个具体的结构体类型。
核心原因:Go的nil接口值没有类型信息
Go里的接口分为两部分:类型(type)和值(value)。当接口变量是nil时,它的类型部分和值部分都是空的。这时候用%T打印,Go就只能输出<nil>,因为没有具体的类型可以展示。
而你打印n的时候能得到int,是因为n是具体的数值类型,不管值是多少,类型都是明确的int,和接口类型的逻辑完全不一样。
怎么解决?获取可能的错误类型
如果想知道这个err在有错误时会是什么类型,或者验证它是否实现了error接口,可以用以下几种方式:
类型断言
你可以尝试把err断言成某个具体的错误类型,比如fmt.Println的错误通常来自标准输出的写入失败,大概率是*os.PathError这类:package main import ( "fmt" "os" ) func main() { _, e := fmt.Println(`He said: "Hello"`) // 尝试断言成os.PathError if err, ok := e.(*os.PathError); ok { fmt.Printf("错误类型是*os.PathError,详情:%v\n", err) } else { fmt.Printf("当前e是%v,不是*os.PathError类型\n", e) } }当然,没有错误的时候
ok会是false,这时候你就知道当前没有具体的错误实例。反射(reflect)
用reflect.TypeOf()可以查看变量的类型,但如果e是nil,它会返回nil:package main import ( "fmt" "reflect" ) func main() { _, e := fmt.Println(`He said: "Hello"`) fmt.Println("reflect.TypeOf(e):", reflect.TypeOf(e)) // 输出 <nil> }只有当
e持有具体错误实例时,reflect.TypeOf()才会返回实际的类型。查看函数文档
其实fmt.Println的错误是底层io.Writer的Write方法返回的,所以你可以去看io.Writer接口的文档,或者os.Stdout(标准输出的类型)的Write方法返回的错误类型,就能知道可能出现的错误类型有哪些。
补充:模拟有错误的场景
如果想亲眼看到有错误时e的类型,你可以临时关闭标准输出(别在生产代码里这么干!):
package main import ( "fmt" "os" ) func main() { // 关闭标准输出 _ = os.Stdout.Close() _, e := fmt.Println("尝试写入已关闭的标准输出") fmt.Printf("有错误时e的值:%v,e的类型:%T\n", e, e) }
这时候输出会是类似:
有错误时e的值:write /dev/stdout: bad file descriptor,e的类型:*os.PathError
就能看到具体的错误类型了。
内容的提问来源于stack exchange,提问作者Natalie Perret

