不一致的指针接收器的nil值(Go bug?)

huangapple go评论77阅读模式
英文:

Inconsistent nil for pointer receiver (Go bug?)

问题

我正在做一个简单的链表接口来学习Go接口,当我遇到这个明显的不一致时。nextT始终为nil,但next()的返回值不是。

package main

import (
    "fmt"
)

type LinkedList interface {
    next() LinkedList
}

type T struct {
    nextT *T
}

func (t *T) next() LinkedList {
    //取消注释以查看差异
    /*if t.nextT == nil {
         return nil
    }*/
    return t.nextT//这是nil!
}

func main() {
    t := new(T)
    fmt.Println(t.nextT == nil)

    var ll LinkedList
    ll = t
    fmt.Println(ll.next() == nil)//为什么这不是nil?
}

没有在next()中进行nil检查(我不应该这样做)时,我得到:

true
false

有了它,我得到了预期的结果:

true
true

我是否发现了一个bug,还是这种意外是有意为之的?在Windows上运行,使用Go版本1,使用zip安装(没有MSI)。

英文:

I was doing a simple linked list interface to learn about Go interfaces when I stumbled upon this apparent inconsistency. nextT is always nil but the return value of next() isn't.

package main

import (
    "fmt"
)

type LinkedList interface {
    next() LinkedList
}

type T struct {
    nextT *T
}

func (t *T) next() LinkedList {
    //uncomment to see the difference
    /*if t.nextT == nil {
         return nil
    }*/
    return t.nextT//this is nil!
}

func main() {
    t := new(T)
    fmt.Println(t.nextT == nil)

    var ll LinkedList
    ll = t
    fmt.Println(ll.next() == nil)//why isn't this nil?
}

Without the nil check (which I shouldn't have to do) in next() I get

true
false

With it I get the expected result

true
true

Have I discovered a bug or is this surprise intentional for some reason? Running on Windows with Go version 1 using the zip installation (no MSI)

答案1

得分: 8

不,那不是一个bug。在Go中,接口基本上是一对两个值:类型信息和指向实际数据的指针。将未类型化的值nil赋给一个接口,也就是接口的零值,意味着该接口既没有类型信息,也没有指向任何存储的数据的指针。

另一方面,将*T指针赋给一个接口,将相应地设置类型信息,并让数据指针指向该指针。在这种情况下,接口不再是nil,因为您已经存储了一个具体类型和具体值。在您的情况下,存储的值恰好是nil。您可以使用类型断言(或reflect包)来检查接口是否具有特定的类型。类型断言只有在类型信息匹配时才会成功(如果之前将nil赋给该接口,则显然永远不可能)。如果测试成功,您将得到一个*T,但是该指针可能仍然具有值nil(对于该类型来说,这是一个有效的值)。

请查看Go标准库中的container/list包,以查看更典型的通用链表实现。还有一篇由Russ Cox撰写的优秀文章,其中详细解释了Go的接口类型。

英文:

No, that's not a bug. An interface in Go, is basically a pair of two values: a type information and a pointer to the actual data. Assigning the untyped value nil to an interface, which also happens to be the zero value of an interface, means that the interface has neither a type information nor a pointer to any data stored.

On the other hand, assigning a *T pointer to an interface, will set the type information accordingly and let the data pointer point to this pointer. In that case, the interface isn't nil anymore, because you have stored a specific type with a specific value inside. In your case it just happens that the value you have stored is nil. You can use a type assertion (or the reflect package) to check if an interface has specific type assigned. The type assertion will only succeed if the type information matches (which can be obviously never be the case if you have assigned nil to that interface before). If the test succeeds, you will get a *T back, but this pointer might still have the value nil (which is a valid value for that type).

Take a look at the container/list package in the Go standard library to see a more idiomatic general linked-list implementation. There is also an excellent article by Russ Cox that contains an in-depth explanation of Go's interface type.

huangapple
  • 本文由 发表于 2012年6月14日 05:29:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/11023593.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定