🦔

nilポインタとして初期化するとはどういうことか

2025/02/01に公開

以下のようなコードを見かけたが、どういう意味なのかわからなかったので調べた。

type User struct {
	Name string
}

func main() {
	user := (*User)(nil)
}

調べてみたところ、userという変数を*User型のnilポインタとして初期化するという記述だった。
nilポインタとは、どこのメモリアドレスも指していないポインタのこと。
ポインタは変数のメモリアドレスを格納するために使用するが、そのポインタに特定のメモリアドレスではなくnilが格納されるとnilポインタになる。

ちなみに、以下のコードはすべてnilポインタと初期化するの変数宣言になる。

var user1 *User
var user2 *User = nil
var user3 = (*User)(nil)
user4 := (*User)(nil)

fmt.Printf("user1: %+v\n", user1)
fmt.Printf("user2: %+v\n", user2)
fmt.Printf("user3: %+v\n", user3)
fmt.Printf("user4: %+v\n", user4)
// 出力
// user1: <nil>
// user2: <nil>
// user3: <nil>

any型の変数にnilポインタが格納された変数を代入した場合、nilとの等価比較はfalseになるので注意が必要。

var iuser1 any = user1
var iuser2 any = user2
var iuser3 any = user3
var iuser4 any = user4

fmt.Printf("iuser1: %+v\n", iuser1)
fmt.Printf("iuser2: %+v\n", iuser2)
fmt.Printf("iuser3: %+v\n", iuser3)
fmt.Printf("iuser4: %+v\n", iuser4)
// 出力
// iuser1: <nil>
// iuser2: <nil>
// iuser3: <nil>
// iuser4: <nil>

fmt.Printf("iuser1 is nil: %v\n", iuser1 == nil)
fmt.Printf("iuser2 is nil: %v\n", iuser2 == nil)
fmt.Printf("iuser3 is nil: %v\n", iuser3 == nil)
fmt.Printf("iuser4 is nil: %v\n", iuser4 == nil)
// 出力
// iuser1 is nil: false
// iuser2 is nil: false
// iuser3 is nil: false
// iuser4 is nil: false

上記が起きる理由は、any型は値と型情報を内部で保持していて、User型の変数が代入されたときに型情報がUserになる。
nilと等価比較を行ったときに型情報がnilでないため、比較の結果がfalseになる。

型情報と値を出力して確認してみた。

// 型と値を出力
fmt.Printf("user1's type: %T\n", user1)
fmt.Printf("user1's value: %v\n", user1)
fmt.Printf("user2's type: %T\n", user2)
fmt.Printf("user2's value: %v\n", user2)
fmt.Printf("user3's type: %T\n", user3)
fmt.Printf("user3's value: %v\n", user3)
fmt.Printf("user4's type: %T\n", user4)
fmt.Printf("user4's value: %v\n", user4)
// 出力
// user1's type: *main.User
// user1's value: <nil>
// user2's type: *main.User
// user2's value: <nil>
// user3's type: *main.User
// user3's value: <nil>
// user4's type: *main.User
// user4's value: <nil>

// nilを代入したany型の変数を定義
var inil any = nil

// nilとの比較結果を出力
fmt.Printf("inil is nil: %v\n", inil == nil)
// 出力
// inil is nil: true

// 型と値を出力
fmt.Printf("inil's type: %T\n", inil)
fmt.Printf("inil's value: %v\n", inil)
// 出力
// inil's type: <nil>
// inil's value: <nil>

参考

Discussion