为什么Go语言有类型化的nil?

39
为什么Go语言使用类型化的nil?它可以为了方便而触发显式接口确认检查。未经类型化的nil有何问题?设计者们打算用类型化的nil解决什么问题?

2
如果Go使用像Swift的Optional<T>那样显式地类型化nil,可能会更好。 - eonil
相关内容,请在此处找到一些推理和解释:隐藏nil值,了解为什么golang失败 - icza
Go语言没有一个有类型的nilnil本身没有类型。然而,nil可以从所处的上下文中获得类型,以便进行直接的比较,例如。 - undefined
4个回答

23

听起来你在询问这个错误信息:

http://play.golang.org/p/h80rmDYCTI

package main

import "fmt"

type A struct {}
type B struct {}

func (a *A) Foo() {
    fmt.Println("A")
}

func (b *B) Foo() {
    fmt.Println("B")
}

func main() {
    n := nil
    n.Foo()
}

这将打印:

prog.go:17: use of untyped nil
 [process exited with non-zero status]

在这个例子中,程序应该打印"A"还是"B"?

你需要帮助编译器做出决定。你可以通过指定n的类型来实现。

例如:

http://play.golang.org/p/zMxUFYgxpy

func main() {
    var n *A
    n.Foo()
}

打印“ A”。

在其他语言中,如果n为nil或其等效物,则n.Foo()可能会立即崩溃。 Go的语言设计者决定让您确定应该发生什么。如果在未检查nil的情况下访问指针,则会获得与其他语言相同的行为。


3
除非你所指的错误信息是"与远程服务器通信出错"(这是我得到的错误信息),否则你可能需要在回答中直接包含更多的信息,而不是通过链接提供。 - Keith Thompson
1
我在这里强调一点,对nil调用类型化方法既有意义又有用。 - Dustin
@KeithThompson 很好的观点。我已经将代码整合进去了。Dustin,很好的观点。虽然与问题无关,但值得简要提及。谢谢你们两个! - Sean
对于那些寻找语法的人:var SomeNil := SomeType(nil)。如果我稍后使用“SomeNil”与一个函数进行比较(这里的SomeType是一个函数),它不起作用,声称“func只能与nil进行比较”。 - Eric Burel

19
这是由于类型安全导致的。在 Go 中,nil 实际上是未初始化变量的值。对于切片、映射、函数、通道、指针和接口的 nil 值不同且不可比较。有关详细信息,请参见语言规范
编辑:正如@newacct所指出的那样,这个技术术语的正确术语是该类型的“零值”

当为值分配内存时,通过声明或调用 make 或 new,但没有提供显式初始化,则该内存会得到默认初始化。此类值的每个元素都设置为其类型的零值:布尔值为 false,整数为 0,浮点数为 0.0,字符串为空串,指针、函数、接口、切片、通道和映射为 nil。

Playground 示例 还有一些关于 nil 接口和错误的信息,可以在Go FAQ中的“为什么我的 nil 错误值不等于 nil?”中找到。

3
这句话的意思是"实际上是未初始化变量的值",技术上称为该类型的“零值”。所有类型都有一个零值。变量不是未初始化的。 - newacct
1
@newacct 我使用语言规范中的术语(http://golang.org/ref/spec),其中指出“未初始化切片的值为`nil`”,“未初始化指针的值为`nil`”等。从同一文档中,我可以看到您正确地指出了我在答案中提到的所有类型的“零值”都是`nil`。我假设“零值”的松散、不精确的定义是“未初始化”,因为Go的作者似乎很乐意在实际规范中同时使用这两个术语。我已更新我的答案,感谢您提供正确的术语。 - Intermernet

9

Go中的所有变量都需要指定类型。使用:=操作符可以从右侧表达式的类型推断出变量的类型。

x := [0]int{}       // var x [0]int
y := make(chan int) // var y chan int
z := map[int]int{}  // var z map[int]int
a := func(int) {}   // var a func(int)
b := 42             // var b int
c := 42.0           // var c float64

对于几乎所有的表达式来说,由于需要在某个地方明确指定类型,或者在未指定时具有默认值,因此它们的类型是明确无误的。唯一的例外是nil

n := nil // var n ???

nil是以下类型的有效值:

  • 指针(Pointers)
  • 不安全指针(Unsafe pointers)
  • 接口(Interfaces)
  • 通道(Channels)
  • 映射(Maps)
  • 切片(Slices)
  • 函数(Functions)

当用户键入nil时,没有一个好的默认类型,因此Golang拒绝此操作,需要明确指定类型。


这个答案听起来对我来说是正确的。不确定为什么它被投下了赞成票。 - byxor

8
没有类型化的nil,你将无法使用短赋值语句:=来表示nil值。
a := nil // Error: use of untyped nil
b := error(nil) // OK

同样,它还允许以下一行代码:
result, err := "A good result", error(nil)

在某些情况下,编写类似上面的内容可能会提供一些方便。


请注意,nil 不是关键字或字面量 - Go 没有内置或标准的类型化 nil 值。 只有在以下情况下才存在类型化 nil:

  • 使用默认(“零值”)初始化声明可空类型的变量
  • nil(直接或间接地)赋给一个类型化的值
  • nil 标识符强制转换为类型(如上例所示)。

这里有一个关于类型化 nil 和接口的微妙之处的剪辑:GopherCon 2015: Kevin Cantwell - What Could Go Wrong?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接