pointers - 隐藏 nil 值,理解为什么 golang 在这里失败

标签 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() {
  // 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 的唯一方法是通过使用反射。

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

Play link here

最佳答案

这里的问题是 showerinterface类型。 Go 中的接口(interface)类型保存实际值及其动态类型。更多详情:The Laws of Reflection #The representation of an interface .
您返回的 slice 包含 2 个非 nil值。第二个值是一个接口(interface)值,一个 (value;type) 对,包含一个 nil指针值和 *display混凝土类型。引自 Go Language Specification: Comparison operators :

Interface values are comparable. Two interface values are equal if they have identical dynamic types and equal dynamic values or if both have value nil.


因此,如果将其与 nil 进行比较,它将是 false .如果将其与代表对 (nil;*display) 的接口(interface)值进行比较,它将是 true :
if x == (*display)(nil) {
    panic("everything ok, nil found")
}
这似乎不可行,因为您必须知道接口(interface)拥有的实际类型。但请注意,您可以使用反射来判断非 nil接口(interface)值包装了一个 nil值使用 Value.IsNil() .你可以在 Go Playground 上看到一个例子。 .
为什么以这种方式实现?
与其他具体类型(非接口(interface))不同的接口(interface)可以保存不同具体类型(不同静态类型)的值。运行时需要知道存储在接口(interface)类型变量中的值的动态或运行时类型。
interface只是一个方法集,如果相同的方法是 method set 的一部分,任何类型都会实现它类型的。有些类型不能是 nil ,例如 struct或自定义类型 int作为其基础类型。在这些情况下,您不需要能够存储 nil该特定类型的值。
但任何类型也包括具体类型,其中 nil是一个有效值(例如 slice 、映射、 channel 、所有指针类型),因此为了在运行时存储满足接口(interface)的值,支持存储 nil 是合理的。界面里面。但除了nil在接口(interface)内部,我们必须将其动态类型存储为 nil value 不携带此类信息。另一种选择是使用 nil当其中存储的值为nil时作为接口(interface)值本身,但这种解决方案是不够的,因为它会丢失动态类型信息。

Some people say that Go's interfaces are dynamically typed, but that is misleading. They are statically typed: a variable of interface type always has the same static type, and even though at run time the value stored in the interface variable may change type, that value will always satisfy the interface.


一般如果要注明nilinterface类型,使用显式 nil值,然后您可以测试 nil平等。最常见的例子是内置的 error type 是一种方法的接口(interface)。只要没有错误,就明确设置或返回值 nil而不是某些具体(非接口(interface))类型错误变量的值(这将是非常糟糕的做法,请参见下面的演示)。
在您的示例中,混淆源于以下事实:
  • 您希望有一个值作为接口(interface)类型 ( shower )
  • 但是您要存储在 slice 中的值不是 shower 类型的但具体类型

  • 所以当你输入 *display输入 shower slice,将创建一个接口(interface)值,它是一对 (value;type) 其中 value 是 nil和类型是 *display .该对内的值将为 nil ,而不是接口(interface)值本身。如果你想放一个 nil值进入 slice ,那么接口(interface)值本身就是 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返回指针,但首先将其转换为接口(interface)类型( error ),因此会创建一个接口(interface)值,其中包含 nil值和类型 *MyErr ,所以接口(interface)值不是 nil .

    关于pointers - 隐藏 nil 值,理解为什么 golang 在这里失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33619954/

    相关文章:

    go - 如何检查Golang中的 map 是否为空?

    c# - 带有接口(interface)列表的接口(interface)

    postgresql - 如何获取 Postgresql 程序警告信息?

    go - 如何对 channel /互斥内存消耗/分配进行基准测试?

    c# - 只读和只写接口(interface)中的自动属性

    java - 接口(interface)作为Java中的方法参数

    c++ - 将变量的指针作为基于模板的参数传递

    C:尝试为结构体动态分配空间、填充数据并将指针分配给结构体

    c - 输出参数中带有指向对象的指针的动态数组

    c++ - Operator [] 重载写/读之间的区别?