Pointers 隐藏零值,理解golang在这里失败的原因

Pointers 隐藏零值,理解golang在这里失败的原因,pointers,go,interface,null,Pointers,Go,Interface,Null,在这种情况下,我无法理解如何正确地确保某些内容不是nil: package main type shower interface { getWater() []shower } type display struct { SubDisplay *display } func (d display) getWater() []shower { return []shower{display{}, d.SubDisplay} } func main() { // SubDis

在这种情况下,我无法理解如何正确地确保某些内容不是
nil

package main

type shower interface {
  getWater() []shower
}

type display struct {
  SubDisplay *display
}

func (d display) getWater() []shower {
  return []shower{display{}, d.SubDisplay}
}

func main() {
  // SubDisplay will be initialized with null
  s := display{}
  // water := []shower{nil}
  water := s.getWater()
  for _, x := range water {
    if x == nil {
      panic("everything ok, nil found")
    }

    //first iteration display{} is not nil and will
    //therefore work, on the second iteration
    //x is nil, and getWater panics.
    x.getWater()
  }
}
我发现检查该值是否实际为
nil
的唯一方法是使用反射

这真的是被通缉的行为吗?或者我没有看到代码中的一些重大错误


这里的问题是
shower
是一种
接口类型。Go中的接口类型保存实际值及其动态类型。有关此的详细信息:

返回的切片包含2个非
nil
值。第二个值是一个接口值,一个(值;类型)对,包含一个
nil
指针值和一个
*显示
具体类型。引述:

界面值具有可比性。如果两个接口值具有相同的动态类型和相同的动态值,或者两者都具有值
nil
,则这两个接口值相等

因此,如果将它与
nil
进行比较,它将是
false
。如果将其与表示该对的接口值进行比较
(nil;*display)
,则为
true

if x == (*display)(nil) {
    panic("everything ok, nil found")
}
这似乎不可行,因为您必须知道接口的实际类型。但请注意,您可以使用反射来判断非
nil
接口值是否使用包装来包装
nil
值。你可以在地图上看到一个例子

为什么要这样实施?

与其他具体类型(非接口)不同的接口可以保存不同具体类型(不同静态类型)的值。运行时需要知道存储在接口类型变量中的值的动态或运行时类型

接口
只是一个方法集,如果相同的方法是该类型的一部分,则任何类型都会实现它。有些类型不能是
nil
,例如
struct
或以
int
为基础类型的自定义类型。在这些情况下,您不需要存储该特定类型的
nil

但任何类型也包括
nil
为有效值的具体类型(例如切片、贴图、通道、所有指针类型),因此为了在运行时存储满足接口要求的值,支持在接口内存储
nil
。但是除了界面内部的
nil
之外,我们还必须存储其动态类型,因为
nil
值不携带此类信息。另一种选择是,当要存储在接口值中的值为
nil
时,使用
nil
作为接口值本身,但此解决方案是不够的,因为它会丢失动态类型信息

有些人说Go的接口是动态类型的,但这是误导。它们是静态类型的:接口类型的变量始终具有相同的静态类型,即使在运行时存储在接口变量中的值可能会更改类型,该值也始终满足接口的要求

通常,如果要为
interface
类型的值指示
nil
,请使用显式
nil
值,然后可以测试
nil
是否相等。最常见的例子是内置类型,它是带有一个方法的接口。只要没有错误,就显式地设置或返回值
nil
,而不是某些具体(非接口)类型错误变量的值(这将是非常糟糕的做法,请参见下面的演示)

在您的示例中,混淆源于以下事实:

  • 您希望有一个值作为接口类型(
    shower
  • 但要存储在切片中的值不是类型
    shower
    ,而是具体类型
因此,当您将
*display
类型放入
shower
切片时,将创建一个接口值,它是一对(value;type),其中value为
nil
,type为
*display
。该对中的值将是
nil
,而不是接口值本身。如果将
nil
值放入片中,那么接口值本身将是
nil
,条件
x==nil
将是
true

演示

请参见此示例:

键入MyErr字符串
func(m MyErr)Error()字符串{
返回“大失败”
}
func doSomething(i int)错误{
开关i{
违约:
返回nil/==nil
案例1:
p*MyErr变量
返回p/!=nil
案例2:
报税表(*MyErr)(无)/!=无
案例3:
p*MyErr变量
返回错误(p)/!=nil,因为接口指向
//零项,但不是零本身。
案例4:
var err error/==nil:接口的零值为nil
return err//这将是真的,因为err已经是接口类型
}
}
func main(){

对于i:=0;i让我们将接口看作指针

假设你有一个指针
a
,它是零,不指向任何东西

var a *int // nil
然后你有一个指针
b
,它指向
a

var b **int
b = &a // not nil
看到发生了什么吗?
b
指向一个不指向任何东西的指针。因此,即使它是链末端的零指针,
b
确实指向某个东西-它不是零

如果查看进程的内存,它可能会如下所示:

address | name | value
1000000 | a    | 0
2000000 | b    | 1000000
请参阅?
a
指向地址0(即
nil
),而
b
指向
a
(1000000)的地址

这同样适用于接口(除了它们看起来有点不同)

与指针一样,指向nil指针的接口本身也不是nil


在这里,请您自己和。

我将采用另一种方式回答您的具体问题,提供您正在寻找的确切答案:

更换止回阀:

if x == nil {
   panic("everything ok, nil found")
}
机智
address | name | value
1000000 | a    | 0
2000000 | b    | 1000000
if x == nil {
   panic("everything ok, nil found")
}
if _, ok := x.(display); !ok {
   panic("everything ok, nil found")
}