在对Go-Micro库改造中,碰到了一个nil与interface比较的问题。现象是我返回的接口是nil,返回值是另一个接口接收,在函数外,与nil进行比较,其竟然不为nil。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
type Err struct {
err string
}

func (e *Err) Error() string {
return e.err
}

func returnErr() *Err {
return nil
}

func main() {
var err error

err = returnErr()
fmt.Println(err, err == nil) // false
}

变体:

1
2
3
4
5
6
7
8
9
10
11
func Foo() error {
var err *os.PathError = nil
// …
return err
}

func main() {
err := Foo()
fmt.Println(err) // <nil>
fmt.Println(err == nil) // false
}

以上两种情况,都有一个共同的操作,就是将一个接口(值为nil),赋值给另一个接口

出现此问题的原因,还是在于interface本身的结构中。(帮助大家了解: Go语言interface底层实现Go Interface 源码剖析

关于nil的讲解:https://speakerdeck.com/campoy/understanding-nil?slide=50

interface由两部分组成:(type, value)。

1
2
3
4
5
var s fmt.Stringer   // Stringer (nil, nil)

// default s = nil

fmt.Println(s == nil) // true

两个成员为(nil, nil),才等于nil。


1
2
3
4
5
var p *Person            // nil of type *Person

var s fmt.Stringer = p // Stringer (*Person, nil)

fmt.Println(s == nil) // false

interface的值为:(*Person, nil),不等于nil。

注意:在编程中,接口赋值给另一个接口,需要注意nil的情况,如果可以,就尽可能避免这种情况。