""
ですが、ポインター、スライス、マップは、チャネル、関数、インターフェイス ゼロ値は 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 値のタイプが異なれば、サイズも異なる場合があります。
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 ビット アーキテクチャの場合、出力サイズは半分になります。