🔧
Go 言語における空の構造体(struct{})の活用テクニック
Go プログラミング
はじめに
Go 言語には空の構造体(struct{})という、一見何もないように見える型があります。しかし、この空の構造体は実は非常に便利で、様々な場面で活用できる優れた機能なのです。
本記事では、以下の活用パターンについて詳しく解説します:
struct{}の基本的な特徴- ゴルーチン間でのシグナル送信
- 集合(Set)のような使い方
- 関数をグループ化するユーティリティ型
実際のコード例を見ながら、メモリ効率が良く、読みやすいコードを書くためのstruct{}活用テクニックをマスターしていきましょう。
1. 空の構造体(struct{})とは?
var s struct{}
fmt.Println(unsafe.Sizeof(s)) // 0
Go の仕様上、空の構造体はメモリサイズが0 バイトとなります。なぜなら、中身が何もないため、メモリ上に場所を確保する必要がないからです。
この「メモリを全く使わない」という特徴を活かすことで、値そのものではなく「存在」や「タイミング」だけを表現したい場合に、非常に効率的なプログラムを作ることができます。
2. ゴルーチン間の通知に使う
複数のゴルーチンが連携して作業する際、「処理が完了したよ」という合図を送りたい場面があります。そんな時にchan struct{}を使うと、データの送受信によるメモリ使用量を完全にゼロにできます。
func main() {
wait := make(chan struct{})
go func() {
fmt.Println("バックグラウンド処理を開始します")
// 何らかの処理...
wait <- struct{}{} // 空の構造体を送信
}()
fmt.Println("処理の完了を待っています...")
<-wait // 送信されるまで待機
fmt.Println("処理が完了しました!")
}
別の方法:close()でチャネルを閉じる
func main() {
wait := make(chan struct{})
go func() {
fmt.Println("バックグラウンド処理を開始します")
// 何らかの処理...
close(wait) // チャネルを閉じて完了を通知
}()
fmt.Println("処理の完了を待っています...")
<-wait // 完了まで待機
fmt.Println("処理が完了しました!")
}
ポイント:
chan struct{}を使うことで、メモリ使用量を最小限に抑えつつ、ゴルーチン間の通知が可能になります。close(wait)でチャネルを閉じると、受信側は即座に処理を続行できます- チャネルの値は使わず、「閉じられた」という事実だけで通知を行います
※チャネルのclose()は一度だけ呼ぶこと(二重クローズはパニックを起こします)
3. 集合(Set)のように使う
Go には他の言語のような「Set 型」が標準では用意されていません。しかし、map[Key]struct{}という形で疑似的な Set を作ることができ、要素の存在確認を高速に行えます。
※集合(Set)とは、重複を許さない要素の集まりを表すデータ構造
// 訪問済みの都市を管理するSet
visited := make(map[string]struct{})
// 要素を追加
visited["tokyo"] = struct{}{}
visited["osaka"] = struct{}{}
// 存在確認
if _, ok := visited["tokyo"]; ok {
fmt.Println("東京は既に訪問済みです")
}
// 要素数を確認
fmt.Printf("訪問済み都市数: %d\n", len(visited))
メリット
- 値の部分(
struct{})はメモリを使わないため、キーだけのメモリ消費
4. 関数をグループ化するユーティリティ型
関連する機能をまとめて整理したい時、空の構造体をメソッドだけを提供する型として使用します。
// ログ出力機能をまとめた型
type Logger struct{}
func (Logger) Info(msg string) {
fmt.Printf("[INFO] %s\n", msg)
}
func (Logger) Error(msg string) {
fmt.Printf("[ERROR] %s\n", msg)
}
func (Logger) Debug(msg string) {
fmt.Printf("[DEBUG] %s\n", msg)
}
// 使用例
func main() {
var log Logger
log.Info("サーバーを起動しました")
log.Error("接続エラーが発生しました")
}
メリット
- 関連する機能を 1 つの型にまとめて整理できる
- インスタンスはメモリを使わないため、気軽に作成可能
- コードの可読性と保守性が向上する
まとめ
空の構造体(struct{})は、Go の「シンプルさ」を体現した機能であり、メモリ使用量を抑えるのでパフォーマンスにも貢献すると思います。