四时宝库

程序员的知识宝库

在Go语言中对接口进行nil检查时要小心

在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。但实际上并非如此。当打印该对象时,它才变成nil。这是如何发生的呢?

原因在于接口具有特殊的实现方式,它包含两个组成部分:类型(Type)和值(Value)。在底层,接口以类型T和值V的形式实现。V是一个具体的值,比如int、struct或指针,但绝不会是接口本身,并且具有类型T。例如,如果我们将值3存储在一个接口中,那么得到的接口值在概念上表示为(T=int,V=3)。值V也被称为接口的动态值,因为在程序执行过程中,给定的接口变量可能会持有不同的V值(和相应的T类型)。

只有当V和T都未设置时,接口值才为nil(T=nil,V未设置)。特别地,一个nil接口总是持有一个nil类型。如果我们将类型为*int的nil指针存储在接口值中,内部类型将始终为*int,而不管指针的值如何(T=*int,V=nil)。因此,即使指针值V为nil,这样的接口值仍然是非nil的。

因此,在上述情况下,创建变量b时,它的类型是*bytes.Buffer,但其值是nil。因此,你会看到上述的输出。让我们看一个更具体的例子,说明何时一个接口值会为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时,它是一个错误(error),但没有具体的错误类型。它的值是nil,因此将其与nil进行比较将返回true。

e is nil
e is <nil>
e is error

在使用接口作为函数参数并进行nil检查时要非常小心,它可能不会返回你所期望的结果。

参考:Be careful about nil check on interface in GoLang | Pixelstech.net

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言