这里的问题是shower 是interface 类型。 Go 中的接口类型保存实际值及其 dynamic 类型。更多详情:The Laws of Reflection #The representation of an interface。
您返回的切片包含 2 个非nil 值。第二个值是一个接口值,一个 (value;type) 对,包含一个 nil 指针值和一个 *display 具体类型。引用自Go Language Specification: Comparison operators:
接口值具有可比性。如果两个接口值具有相同的动态类型和相同的动态值,或者两者都具有值nil,则它们是相等的。
因此,如果您将其与nil 进行比较,它将是false。如果将其与表示 (nil;*display) 对的接口值进行比较,它将是 true:
if x == (*display)(nil) {
panic("everything ok, nil found")
}
这似乎不可行,因为您必须知道接口的实际类型。但请注意,您可以使用反射来判断非nil 接口值是否使用Value.IsNil() 包装nil 值。您可以在 Go Playground 上查看此示例。
为什么要这样实现?
与其他具体类型(非接口)不同的接口可以保存不同具体类型(不同静态类型)的值。运行时需要知道存储在接口类型变量中的值的动态或运行时类型。
interface 只是一个方法集,任何类型如果相同的方法是该类型的 method set 的一部分,则实现它。有些类型不能是nil,例如struct 或以int 作为其基础类型的自定义类型。在这些情况下,您不需要能够存储该特定类型的 nil 值。
但任何类型还包括具体类型,其中nil 是有效值(例如切片、映射、通道、所有指针类型),因此为了在运行时存储满足接口支持在接口内部存储nil是合理的。但除了接口内部的nil,我们必须存储它的动态类型,因为nil 值不携带此类信息。当要存储的值是nil 时,另一种选择是使用nil 作为接口值本身,但是这种解决方案是不够的,因为它会丢失动态类型信息。
有人说 Go 的接口是动态类型的,但这是误导。它们是静态类型的:接口类型的变量始终具有相同的静态类型,即使在运行时存储在接口变量中的值可能会改变类型,该值也将始终满足接口。
一般来说,如果您想为interface 类型的值指示nil,请使用显式nil 值,然后您可以测试nil 是否相等。最常见的例子是内置的error 类型,它是一种方法的接口。只要没有错误,您就显式设置或返回值nil,而不是某些具体(非接口)类型错误变量的值(这将是非常糟糕的做法,请参见下面的演示)。
在您的示例中,混淆源于以下事实:
- 您希望将值作为接口类型 (
shower)
- 但您要存储在切片中的值不是
shower 类型,而是具体类型
所以当你将*display 类型放入shower 切片中时,将创建一个接口值,它是一对(值;类型),其中值是nil,类型是*display。对中的 value 将是nil,而不是接口值本身。如果您将nil 值放入切片中,则接口值本身 将为nil,条件x == nil 将为true。
演示
请看这个例子:Playground
type MyErr string
func (m MyErr) Error() string {
return "big fail"
}
func doSomething(i int) error {
switch i {
default:
return nil // == nil
case 1:
var p *MyErr
return p // != nil
case 2:
return (*MyErr)(nil) // != nil
case 3:
var p *MyErr
return error(p) // != nil because the interface points to a
// nil item but is not nil itself.
case 4:
var err error // == nil: zero value is nil for the interface
return err // This will be true because err is already interface type
}
}
func main() {
for i := 0; i <= 4; i++ {
err := doSomething(i)
fmt.Println(i, err, err == nil)
}
}
输出:
0 <nil> true
1 <nil> false
2 <nil> false
3 <nil> false
4 <nil> true
在情况 2 中,返回了一个 nil 指针,但首先它被转换为一个接口类型 (error),因此创建了一个接口值,其中包含一个 nil 值和类型 *MyErr,所以接口值不是nil。