在 Go 语言中,特别是在错误检查方面,经常会看到nil 检查,这是由于 Go 语言的特殊错误处理约定。在大多数情况下,nil 检查是直截了当的,但在接口情况下,需要特别小心。
以下是一个代码片段,猜猜它的输出会是什么:
package main
import (
"bytes"
"fmt"
"io"
)
func check(w io.Writer) {
if w != nil {
fmt.Println("w is not nil")
}
fmt.Printf("w is %+v\n", w)
}
func main() {
var b *bytes.Buffer
check(b)
fmt.Printf("b is %+v", b)
}
w is not nil
w is
b is
在check()
方法中,你可能期望w
是nil
,但实际上并非如此。当打印该对象时,它显示为空。这是如何发生的呢?
Go 语言中的接口具有特殊的实现方式,它包含两个组成部分:
接口的底层表示是 (T, V)
的形式:
int
、struct
或 *bytes.Buffer
)。只有当 T 和 V 都未设置时,接口值才会被认为是nil
(即 T=nil, V=nil
)。
特别地:
*int
的nil
指针,则接口的类型为*int
,而值为nil
。在这种情况下,接口值并不是nil
。当创建变量 b
时,它的类型是 *bytes.Buffer
,但它的值是 nil
。因此:
check(b)
时,接口值 (T=*bytes.Buffer, V=nil)
并不为nil
,所以 w != nil
条件为真。w
的值是空,但类型信息仍然存在。以下代码进一步说明接口值什么时候为nil
:
package main
import (
"fmt"
)
type SomeError struct{}
func (se *SomeError) Error() string {
return "error"
}
func check(e error) {
if e == nil {
fmt.Println("e is nil")
}
fmt.Printf("e is %+v\n", e)
}
func main() {
var e error = nil
check(e)
var se *SomeError = nil
check(se)
}
e is nil
e is <nil>
e is error
第一部分:var e error = nil
e
没有具体的类型,且值为 nil
。e == nil
为真。第二部分:var se *SomeError = nil
e
持有类型 *SomeError
和值 nil
。e == nil
为假,因为 e
的类型部分存在。在使用接口作为函数参数并进行nil
检查时,要特别小心。如果接口的类型部分存在,即使值为nil
,接口本身也不是nil
。
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.