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")
}