Goのインターフェースでnilがnilでない時

[![表紙](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967563/208e9db9-bcce-4635-8a8c-5f5ba7d71c37.png)](https://leapcell.io/?lc_t=q_gonilnoeq)

Go 言語でコーディングをしていると、ある特定の状況に遭遇することがあります。つまり、インターフェース型（たとえば `any` や `interface{}`）の引数で他の値を受け取るとき、与えられた引数が明らかに `nil` であるにもかかわらず、`x == nil` という不等式が成立しない場合があります。これはなぜでしょうか？本記事ではその謎を解き明かします。

## ケーススタディ

```go
package main

import "fmt"

func main() {
  var a any = nil
  var b *int = nil
  fmt.Println("isNil: ", isNil(a))
  fmt.Println("isNil: ", isNil(b))
  fmt.Println("b == nil: ", b == nil)
}

func isNil(x any) bool {
  return x == nil
}
```

プログラムの実行結果：

```
isNil:  true
isNil:  false
b == nil:  true
```

上記の Go コードの例は、`any`（すなわち `interface{}`）型を使用したときに、変数が `nil` かどうかを判断する際の微妙な違いを示しています。特に、ある状況下では、値が `nil` であるにもかかわらず、`any` 型の変数は `nil` として扱われない理由を示しています。

このコード例では、まず `isNil` 関数を定義し、その引数に `x any` を使用しています。そして `main` 関数では、2 つの異なる型の変数 `a` と `b` を初期化し、いずれも `nil` を代入しています。次に、それぞれの変数を引数として `isNil` 関数を呼び出しています。また、`b == nil` の値を直接出力し、比較しています。

実行結果を見ると、`isNil` 関数内では `a == nil` は `true` ですが、`b == nil` は `false` となっています。一方、外部では `b == nil` は `true` となっています。これは、`isNil` 関数内で `any` 型の変数を使っているため、`x == nil` が成立しないという現象です。この仕組みを理解するためには、`any` の内部構造について知る必要があります。詳細は以下をご覧ください。

## any(interface{}) の内部構造

Go 言語において、`any` は `interface{}` の別名です。`interface{}` 型の値は、内部的には **型情報** と **値情報** の 2 つの部分から構成されています。

- **型情報（Type Part）**：インターフェースが保持する具体的な型です。たとえば、インターフェースが `*int` 型の値を保持している場合、この型情報は `*int` になります。
- **値情報（Value Part）**：インターフェースが保持する具体的な値です。値が整数 `3` であればそれが保存され、`nil` であれば `nil` が保存されます。

値をインターフェース型（例：`any`）に代入する際、インターフェースはその値の「型」と「具体的な値」の両方を内部に保持します。そして、インターフェースが `nil` とみなされるのは、「型情報」と「値情報」の**両方が `nil`** である場合のみです。

先ほどのコード例を思い出してください。変数 `b`（型は `*int`、値は `nil`）をインターフェース型の変数 `x` に代入した時、`x` の内部構造は `type = *int`、`value = nil` という状態になります。このとき、型情報が `nil` ではないため、`x == nil` は**成立しません**。

## リフレクションを使って nil をチェックする

`==` や `!=` といった比較だけでは、インターフェース型の変数が本当に `nil` かどうかを完全には判断できないことが分かりました。それでは、どうすれば正確に判定できるのでしょうか？
その答えは、**リフレクション（反射）機構を使うこと**です。

リフレクションを使えば、変数の値が `nil` であるかどうかを正確に調べることができます。

```go
func isNil(x any) bool {
  if x == nil {
    return true
  }
  value := reflect.ValueOf(x)
  switch value.Kind() {
  case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer:
    return value.IsNil()
  default:
    return false
  }
}
```

このように、`reflect.ValueOf` を使って変数の具体的な型や値を取得し、さらに `Kind()` を使ってそのカテゴリを判定したうえで `IsNil()` を呼び出せば、より正確な `nil` 判定が可能になります。

## まとめ

本記事では、Go 言語において、インターフェース型の変数が `nil` の値を持っているにもかかわらず、`nil` と等しくないと判定される理由について深く掘り下げました。具体的なコード例と、`any`（つまり `interface{}`）の内部構造の解説を通じて、この現象の本質を明らかにしました。

重要なポイントは以下のとおりです：

- **インターフェース型の内部構造**：`any`（すなわち `interface{}`）の内部は、「型情報」と「値情報」の 2 つから構成されている。**この 2 つが同時に `nil` のときに限り、そのインターフェースは `nil` とみなされる**。
- **解決策**：`reflect` パッケージを使うことで、インターフェース型の変数が本当に `nil` かどうかを正確に判断することができる。

このような Go 言語特有の仕様を理解しておくことで、予期せぬバグや混乱を避けることができ、より堅牢で正確なプログラムを書くことが可能になります。

---

### [私たちはLeapcell、Goプロジェクトのホスティングの最適解です。](https://leapcell.io/?lc_t=q_gonilnoeq)

[![Leapcell](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967563/d11d4509-d7e9-4915-a7d5-6f4cbd8f28fe.png)](https://leapcell.io/?lc_t=q_gonilnoeq)

[Leapcell](https://leapcell.io/?lc_t=q_gonilnoeq)は、Webホスティング、非同期タスク、Redis向けの次世代サーバーレスプラットフォームです：

**複数言語サポート**

- Node.js、Python、Go、Rustで開発できます。

**無制限のプロジェクトデプロイ**

- 使用量に応じて料金を支払い、リクエストがなければ料金は発生しません。

**比類のないコスト効率**

- 使用量に応じた支払い、アイドル時間は課金されません。
- 例: $25で6.94Mリクエスト、平均応答時間60ms。

**洗練された開発者体験**

- 直感的なUIで簡単に設定できます。
- 完全自動化されたCI/CDパイプラインとGitOps統合。
- 実行可能なインサイトのためのリアルタイムのメトリクスとログ。

**簡単なスケーラビリティと高パフォーマンス**

- 高い同時実行性を容易に処理するためのオートスケーリング。
- ゼロ運用オーバーヘッド — 構築に集中できます。

[ドキュメントで詳細を確認！](https://docs.leapcell.io/)

[![Try Leapcell](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967563/f44fffab-6ba5-429b-bd84-4866d3062d59.png)](https://leapcell.io/?lc_t=q_gonilnoeq)

Xでフォローする：[@LeapcellHQ](https://x.com/LeapcellHQ)

---

[ブログでこの記事を読む](https://leapcell.io/blog/the-trap-of-nil-in-go-interface)