当前位置: 首页 > 面试题库 >

隐藏nil值,了解golang为什么会失败

阳俊德
2023-03-14
问题内容

我无法理解如何正确确保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是一种interface类型。Go中的接口类型保存实际值及其 动态
类型。关于此的更多详细信息:反射定律#接口的表示。

您返回的切片包含2个非nil值。第二个值是一个接口值,一个保存nil指针值的(值;类型)对和一个*display具体类型。从Go语言规范中引用:比较运算符:

接口值是可比较的。如果两个接口值具有相同的动态类型和相等的动态值,或者两个值都具有value,则它们是相等的nil

所以,如果你把它比作nil,这将是false。如果将其与代表该对的接口值进行比较(nil;*display),则将为true

if x == (*display)(nil) {
    panic("everything ok, nil found")
}

这似乎不可行,因为您必须知道接口所持有的实际类型。但是请注意,您可以使用反射来判断非nil接口值是否使用来包装nilValue.IsNil()。您可以在Go
Playground
上看到一个示例。

为什么以这种方式实施?

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

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

但是 任何类型
还包括nil有效值(例如切片,地图,通道,所有指针类型)的具体类型,因此,为了在运行时存储满足接口要求的值,可以合理地支持nil在接口内部进行存储。但是除了nil接口内部,我们还必须存储其动态类型,因为该nil值不包含此类信息。nil当要存储在其中的值是时,替代选项将用作接口值本身nil,但是此解决方案不足,因为它将丢失动态类型信息。

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

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

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

  • 您想要一个值作为接口类型(shower
  • 但是您要html" target="_blank">存储在切片中的值不是类型shower而是具体类型

因此,当您将一个*display类型放入shower切片中时,将创建一个接口值,该接口值是一对(value;
type),其中value是nil且type是*display。该对中的
将是nil,而不是接口值本身。如果您将一个nil值放入分片中,则接口值 本身nil,条件x == niltrue

示范

请参见以下示例:操场

type MyErr string

func (m MyErr) Error() string {
    return "big fail"
}

func doSomething(i int) error {
    switch i {
    default:
        return nil // This is the trivial true case
    case 1:
        var p *MyErr
        return p // This will be false
    case 2:
        return (*MyErr)(nil) // Same as case 1
    case 3:
        var err error // Zero value is nil for the interface
        return err    // This will be true because err is already interface type
    case 4:
        var p *MyErr
        return error(p) // This will be false because the interface points to a
                        // nil item but is not nil itself.
    }
}

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> true
4 <nil> false

在情况2
nil中,返回了一个指针,但首先将其转换为接口类型(error),因此创建了一个接口值,其中包含一个nil值和该类型*MyErr,因此该接口值不是nil



 类似资料:
  • 问题内容: 看到这个游乐场:http : //play.golang.org/p/nWHmlw1W01 为什么两个函数的输出不同? 问题答案: 诚然,这有点古怪,但是有一个解释。 想象一个变量是由两个字段组成的结构:一个是类型,另一个是数据。(和)。实际上,它看起来就像Go运行时中的样子。 当您将nil slice传递给时,只会将其作为值传递,因此您的比较可简化为。 同时,调用会自动将您的变量包装

  • 请原谅我的无知,我恐怕错过了一些基本的选项理解。我的印象是(隐式展开的可选指示符)保证该类型的变量不为零。然而,这个非常简单的Apple API很少会返回我。 这是一个意外的错误还是Optionals规范的一部分?因为如果这是规范的一部分,我不明白为什么会有选项,而不是变量可以存在或。

  • 问题内容: 很多情况下在golang中使用 nil 。例如: 但是我们不能这样使用它: Golang编译器将引发错误。 看起来只能用于struct和interface的指针。如果是这样, 那意味着什么? 而 当我们用它来比较其他对象,他们如何比较,换句话说,如何golang确定一个对象是零 ? 编辑:例如,如果接口为nil,则其类型和值必须同时为nil。 golang如何做到这一点? 问题答案:

  • 我被这个概念困住了。 这是我在一个站点上看到的解释的一部分: 隐藏实现 我是这样想象的: 库客户机是否知道这个实现有什么区别?

  • 问题:如果距离为空/null,我试图隐藏一个值。 我尝试了以下操作,但该值仍在显示: 和 什么是实现我所期待的正确方法。 以下是我在coldfusion中定义距离的方式: 当我做以下建议时: ng-show="e.distance===未定义" 当我执行其他建议时,例如ng hide=“e.distance”, 以下是提供商包含的内容:

  • 我理解不同的行为(与覆盖相比是新的),但不理解术语。