13
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Go7Advent Calendar 2019

Day 18

nil について調べ始めた話

Last updated at Posted at 2019-12-17

はじめに

この記事は Go7 Advent Calendar 2019 の 18 日目の記事です。
Go さん nil さんの気持ちをもうちょっと理解してみようと思ったところを発端にソースコードを読み始めた記録です。
見返してみたらとても当たり前なことしか述べておりませんでした (´·ω·`)

本記事は go 1.13 を前提にしています。

TL;DR

  • nil 周りのソースコード読んでみた
  • nil って何?という話と nil っぽい何かが 2 つ見つかったけど何?という話
  • 目新しい話はありません(´·ω·`)

nil の登場

nil らしきものの姿は src/go/types/universe.go にて見つけることができます。https://golang.org/ref/spec#Blocks にも記載のある universe block がスコープになっています。
ただ、その近辺でも nil が使われていたりしてよくわかりません。

もう少し追ってみると、このあたり(builtin パッケージ) で下記のように定義されています。

    var nil Type // Type must be a pointer, channel, func, interface, map, or slice type
    ...
    type Type int

これは、

/*
	Package builtin provides documentation for Go's predeclared identifiers.
	The items documented here are not actually in package builtin
	but their descriptions here allow godoc to present documentation
	for the language's special identifiers.
*/

とある通り、ドキュメンテーションであってどこからも参照されていません。
これをそのまま受け取ると、 nil は Type 型の初期値 というように理解できます。
なんとなく外れていなさそうな雰囲気です。

初期値とは

では Type 型の初期値 とは一体どなた様でしょうか。ふんいきしかわからない。
コンパイラ側のソースコードを読んでみると、こちらのコードに辿り着きました。


    // zeroVal returns the zero value for type t.
    func (s *state) zeroVal(t *types.Type) *ssa.Value {
    	switch {
    ...
        case t.IsPtrShaped():
    		return s.constNil(t)
    	case t.IsBoolean():
    		return s.constBool(false)
    	case t.IsInterface():
    		return s.constInterface(t)
    	case t.IsSlice():
    		return s.constSlice(t)
    	case t.IsStruct():
    		n := t.NumFields()
    		v := s.entryNewValue0(ssa.StructMakeOp(t.NumFields()), t)
    		for i := 0; i < n; i++ {
    			v.AddArg(s.zeroVal(t.FieldType(i)))
    		}
    		return v
    ...

前述の builtin パッケージには、 var nil Type // Type must be a pointer, channel, func, interface, map, or slice type とありますので、これを素直に受け取ると

    case t.IsPtrShaped():
    		return s.constNil(t)

で、 s.constNil(t) が正体のように見えます。

二つの nil

話は nil らしきものの姿 src/go/types/universe.go に戻ります。こちら、見てみると


    var Typ = []*Basic{
    ...
    UntypedNil:     {UntypedNil, IsUntyped, "untyped nil"},
    ...
    }
    ...
    func defPredeclaredTypes() {
    	for _, t := range Typ {
    		def(NewTypeName(token.NoPos, nil, t.name, t))
    	}
    ...

と、


    func defPredeclaredNil() {
    	def(&Nil{object{name: "nil", typ: Typ[UntypedNil], color_: black}})
    }

の、nil っぽい何かが 2 つあるようです。

これらの違いは、値か型か、ということがコードからわかります。


    func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, wantType bool) {
    ...
    switch obj := obj.(type) {
    ...
    case *TypeName:
    		x.mode = typexpr
    ...
    case *Nil:
    		x.mode = value
    ...

UntypedNil は、例えば、


    var x = nil
    // use of untyped nil

とすると会うことができます。

他にも、下記のようにしても会えます。


    var x interface{}
    x = nil // <- untyped nil の代入

interface だけ ok なのは、このあたりのコード に記述されています。


    case *Interface:
    		if !x.isNil() && !t.Empty() /* empty interfaces are ok */ {
    			goto Error
    		}
    		// Update operand types to the default type rather then
    		// the target (interface) type: values must have concrete
    		// dynamic types. If the value is nil, keep it untyped
    		// (this is important for tools such as go vet which need
    		// the dynamic type for argument checking of say, print
    		// functions)
    		if x.isNil() {
    			target = Typ[UntypedNil]
    		} else {
    			// cannot assign untyped values to non-empty interfaces
    			if !t.Empty() {
    				goto Error
    			}
    			target = Default(x.typ)
    		}

おわりに

その他、いろいろ読んでみて、あ!これ前にXXでやったやつだ!みたいな発見も結構あったのですがただでさえまとまっていない内容がさらにまとまらなくなるので割愛します。
今までよりほんのちょっとだけ Go さん nil さんの気持ちがわかったような気がしました。

13
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?