Go構造体の「_」フィールド、追加したら意外な結果に？

[![表紙](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967563/db707db3-71ec-4b66-9329-b8649a124d96.png)](https://leapcell.io/?lc_t=q_gounderline)

## 前書き

Go 言語では、アンダースコア（ \_ ）の使用をよく目にします。たとえば、不要な変数を無視するためのプレースホルダーとして使用したり、使用しないパッケージを副作用のためにインポートしたり（いわゆる side-effects のための import）、また型変換時に変数を無視する場面などです。

しかし、多くの人は構造体の中でアンダースコア（ \_ ）が使用されているのを見たことがないかもしれません。たとえば、 \_ という名前の構造体フィールドを定義するケースです。

では、このようなフィールドを定義する目的は何でしょうか？

## アンダースコア（ \_ ）フィールドの有無による構造体コードの比較

まず、アンダースコア（ \_ ）フィールドを含まない構造体のコード例を見てみましょう。

`model` パッケージ内に、`Name` と `Age` の 2 つのフィールドを持つ `User` 構造体を定義します。

```go
type User struct {
    Name string
    Age  int
}
```

位置による初期化とフィールド名を明示した初期化の 2 つの方法で構造体変数を宣言します。

```go
user := model.User{"Alice", 18}
user = model.User{Name: "Alice", Age: 18}
```

上記のコードでは、構造体の定義でも宣言でも何の問題もありません。

次に、アンダースコア（ \_ ）フィールドを含む構造体のコード例を見てみましょう。

`model` パッケージ内に、`Name`、`Age`、および `_` の 3 つのフィールドを持つ `User` 構造体を定義します。

```go
type User struct {
    Name string
    Age  int
    _   struct{}
}
```

こちらも、位置による初期化とフィールド名を明示した初期化の 2 つの方法で構造体変数を宣言します。

```go
// コンパイルエラー too few values in struct literal of type model.User
user := model.User{"Alice", 18}
// コンパイルエラー implicit assignment to unexported field _ in struct literal of type model.User
user = model.User{"Alice", 18, struct{}{}}
// 正常
user = model.User{}
user = model.User{Name: "Alice", Age: 18}
```

上記の例では、`user := model.User{"Alice", 18}` や `model.User{"Alice", 18, struct{}{}}` のような位置による初期化ではコンパイルエラーが発生しますが、ゼロ値による初期化やフィールド名を指定した初期化では問題なく動作します。

アンダースコア（ \_ ）フィールドの有無による構造体コードの比較から分かるように、構造体に \_ という名前のフィールドを定義することで、その構造体の初期化時にフィールド名を明示した初期化を強制することができます（ゼロ値で構造体変数を宣言する場合を除く）。

## 原理の簡単な解析

位置による初期化で構造体を宣言する場合、構造体のフィールドの順番に従ってすべてのフィールドの値を順番に提供する必要があります。

構造体の中に `_` という名前のフィールドを定義すると、位置による初期化の際に `_` フィールドの値が提供されていなければ、コンパイラは「too few values in struct literal of type XXX」というエラーを出します。これは、すべての構造体フィールドの値が提供されていないためです。

仮に構造体フィールドの順番通りにすべてのフィールドの値を提供したとしても、コンパイラは「implicit assignment to unexported field \_ in struct literal of type XXX」というエラーを出します。これは `_` フィールドの先頭文字が大文字でないため、エクスポートされていないフィールド（非公開フィールド）と見なされ、暗黙的に代入することができないためです。その結果、位置による初期化での代入はできません。

以上から、位置による初期化ではこの構造体の変数を宣言することができず、ゼロ値による初期化やフィールド名を明示した初期化でのみ構造体変数を宣言できるということが分かります。

## まとめ

本記事では、Go 言語における構造体フィールドの命名にアンダースコア（ \_ ）を使用するという特殊な用途について解説しました。

具体的には、`_` という名前のフィールドを定義することで、開発者に対して構造体の初期化時にフィールド名を明示した初期化を強制することができます（ゼロ値での初期化を除く）。

このようにすることで得られる利点には以下のようなものがあります：

- **コードの可読性**：フィールド名を明示した初期化は、各値がどのフィールドに対応しているのかが明確になり、コードの可読性と保守性が向上します。
- **エラーの回避**：位置による初期化は、フィールドの順番に厳密に従う必要があり、ミスを誘発しやすいですが、フィールド名を明示する初期化であればそのような問題を回避できます。

---

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

[![Leapcell](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967563/1f57d7fb-cbb9-4935-a269-4369f59e3ee4.png)](https://leapcell.io/?lc_t=q_gounderline)

[Leapcell](https://leapcell.io/?lc_t=q_gounderline)は、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/98f9f00a-fa2b-45dd-b996-4cfd36270964.png)](https://leapcell.io/?lc_t=q_gounderline)

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

---

[ブログでこの記事を読む](https://leapcell.io/blog/why-varchar-field-instead-of-text-in-mysql)