Go 言語 nil: null/ゼロ値

 
 
Go 言語では、ブール型のゼロ値 (初期値) は false、数値型のゼロ値は 0、文字列型のゼロ値は空の文字列""ですが、ポインター、スライス、マップは、チャネル、関数、インターフェイス ゼロ値は nil です。

 

nil は Go 言語で定義済みの識別子です。他のプログラミング言語の開発経験のある開発者は、他の言語では nil を null (NULL) とみなす可能性があります。実際、これは完全に正しいわけではありません。Go 言語では nil は null とは異なるためです。他の言語もいろいろな意味で。

いくつかの側面を通して Go 言語の nil を紹介しましょう。

nil 識別子は比較できません

package main 

import( 
    ”fmt” 
) 

func main(){ 
    fmt.Println(nil == nil) 
} 

実行結果は次のとおりです。

PS D:\code> go run .\main.go
# command-line-arguments
.\main.go:8:21: invalid operation: nil == nil (operator == not defined on nil)

これは、2 つの None 値が常に等しい Python などの動的言語とは異なります。

>>> None == None
True

上記の演算結果から、 == nil に対する未定義の演算であることを理解するのは難しくありません。

nil はキーワードまたは予約語ではありません

nil は Go 言語のキーワードや予約語ではありません。つまり、次のような nil という名前の変数を定義できます。

var nil = errors.New(“my god”)

上記の宣言ステートメントはコンパイルできますが、コンパイルすることはお勧めできません。

nil にはデフォルトの型がありません

package main

import (
    "fmt"
)

func main(){
    fmt.Printf("%T", nil)
    print(nil)
} 

実行結果は次のとおりです。

PS D:\code> go run .\main.go
# command-line-arguments
.\main.go:9:10: use of untyped nil

異なるタイプの nil ポインターは同じです

package main

import (
    "fmt"
)

func main() {
    var arr []int
    var num *int
    fmt.Printf("%p\n", arr)
    fmt.Printf("%p", num)
} 

実行結果は次のとおりです。

PS D:\code> go run .\main.go
0x0
0x0

実行結果から、arr と num のポインタが両方とも 0x0 であることがわかります。

異なるタイプの nil は比較できません

 package main

import (
    "fmt"
)

func main() {
    var m map[int]string
    var ptr *int
    fmt.Printf(m == ptr)
} 

実行結果は次のとおりです。

PS D:\code> go run .\main.go
# command-line-arguments
.\main.go:10:20: invalid operation: arr == ptr (mismatched types []int and *int)

同じ型の 2 つの nil 値も比較できない場合があります

Go 言語では、マップ型、スライス型、関数型の nil 値を比較することはできません。比較できない型の 2 つの値を比較することは違法です。次のステートメントはコンパイルできません。

package main

import (
    "fmt"
)

func main() {
    var s1 []int
    var s2 []int
    fmt.Printf(s1 == s2)
} 

実行結果は次のとおりです。

PS D:\code> go run .\main.go
# command-line-arguments
.\main.go:10:19: invalid operation: s1 == s2 (slice can only be compared to nil)

上記のエラー メッセージからわかるように、上記の比較不可能な型の null 値は、次のように nil 識別子と直接比較できます。

package main

import (
    "fmt"
)

func main() {
    var s1 []int
    fmt.Println(s1 == nil)
} 

実行結果は次のとおりです。

PS D:\code> go run .\main.go
true

nil はマップ、スライス、ポインタ、チャネル、関数、インターフェイスのゼロ値です

package main

import (
    "fmt"
)

func main() {
    var m map[int]string
    var ptr *int
    var c chan int
    var sl []int
    var f func()
    var i interface{}
    fmt.Printf("%#v\n", m)
    fmt.Printf("%#v\n", ptr)
    fmt.Printf("%#v\n", c)
    fmt.Printf("%#v\n", sl)
    fmt.Printf("%#v\n", f)
    fmt.Printf("%#v\n", i)
} 

実行結果は次のとおりです。

PS D:\code> go run .\main.go
map[int]string(nil)
(*int)(nil)
(chan int)(nil)
[]int(nil)
(func())(nil)
<nil>

ゼロ値は、変数が宣言されても初期化されていない後で、Go で変数に割り当てられる型のデフォルト値です。

異なる種類の nil 値によって占有されるメモリ サイズは異なる場合があります

型のすべての値のメモリ レイアウトは同じであり、nil も例外ではなく、nil のサイズは同じ型内の非 nil 型のサイズと同じです。ただし、nil 値のタイプが異なれば、サイズも異なる場合があります。

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    var p *struct{}
    fmt.Println( unsafe.Sizeof( p ) ) // 8

    var s []int
    fmt.Println( unsafe.Sizeof( s ) ) // 24

    var m map[int]bool
    fmt.Println( unsafe.Sizeof( m ) ) // 8

    var c chan string
    fmt.Println( unsafe.Sizeof( c ) ) // 8

    var f func()
    fmt.Println( unsafe.Sizeof( f ) ) // 8

    var i interface{}
    fmt.Println( unsafe.Sizeof( i ) ) // 16
} 

実行結果は次のとおりです。

PS D:\code> go run .\main.go
8
24
8
8
8
16

具体的なサイズはコンパイラとアーキテクチャによって異なりますが、上記の出力結果は 64 ビット アーキテクチャおよび標準コンパイラで完成しますが、32 ビット アーキテクチャの場合、出力サイズは半分になります。

 

「 Go 言語 nil: null/ゼロ値」についてわかりやすく解説!絶対に観るべきベスト2動画

Go言語とは?|プログラミング言語のGo言語について3分でわかりやすく解説します【プログラミング初心者向け】
How to Fix N/A, Null, 0… in Looker Studio