Goの空構造体の無限大な使用法
Grace Collins
Solutions Engineer · Leapcell

序文
Goプログラミング言語には、多くの人々を混乱させる可能性のある特別な用法、つまり空の構造体struct{}
があります。この記事では、Goの空の構造体について詳しく説明します。準備はできましたか?お気に入りの飲み物やお茶を用意して、飛び込みましょう。
空の構造体とは
フィールドを含まない構造体は、空の構造体と呼ばれます。これは、次の2つの方法で定義できます。
- 匿名空の構造体
var e struct{}
- 名前付き空の構造体
type EmptyStruct struct{} var e EmptyStruct
空の構造体の特性
空の構造体には、次の主な特性があります。
- ゼロメモリ割り当て
- 同じアドレス
- ステートレス
ゼロメモリ割り当て
空の構造体は、メモリ空間を占有しません。これにより、メモリの最適化に非常に役立ちます。実際にゼロメモリを占有するかどうかを確認する例を見てみましょう。
package main import ( "fmt" "unsafe" ) func main() { var a int var b string var e struct{} fmt.Println(unsafe.Sizeof(a)) // 4 fmt.Println(unsafe.Sizeof(b)) // 8 fmt.Println(unsafe.Sizeof(e)) // 0 }
出力が示すように、空の構造体のメモリサイズは0です。
同じアドレス
作成する空の構造体の数に関係なく、それらはすべて同じアドレスを指します。
package main import ( "fmt" ) func main() { var e struct{} var e2 struct{} fmt.Printf("%p\n", &e) // 0x90b418 fmt.Printf("%p\n", &e2) // 0x90b418 fmt.Println(&e == &e2) // true }
ステートレス
空の構造体にはフィールドが含まれていないため、状態を保持できません。これにより、ステートレスオブジェクトまたは条件を表すのに非常に役立ちます。
ゼロメモリと同一アドレスの理由
空の構造体がゼロサイズで同じアドレスを共有する理由を理解するには、Goのソースコードを詳しく調べる必要があります。
/go/src/runtime/malloc.go
// base address for all 0-byte allocations var zerobase uintptr func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { // ... if size == 0 { return unsafe.Pointer(&zerobase) } // ...
malloc.go
からのこの抜粋によると、割り当てるオブジェクトのサイズが0の場合、zerobase
へのポインタを返します。zerobase
は、ゼロバイトオブジェクトの割り当てに使用されるベースアドレスであり、実際にはメモリ空間を占有しません。
空の構造体の使用シナリオ
空の構造体は、主に次の3つのシナリオで使用されます。
- Setデータ構造の実装
- チャネル内のシグナルとして使用
- メソッドレシーバーとして使用
Setデータ構造の実装
Goには組み込みのSet型はありませんが、map型を使用して実装できます。mapキーは一意であるため、要素をキーとして格納し、値は重要ではないため、メモリを節約するために、空の構造体を値として使用します。
package main import "fmt" type Set[K comparable] map[K]struct{} func (s Set[K]) Add(val K) { s[val] = struct{}{} } func (s Set[K]) Remove(val K) { delete(s, val) } func (s Set[K]) Contains(val K) bool { _, ok := s[val] return ok } func main() { set := Set[string]{} set.Add("Leapcell") fmt.Println(set.Contains("Leapcell")) // true set.Remove("Leapcell") fmt.Println(set.Contains("Leapcell")) // false }
チャネルシグナルとして使用
空の構造体は、特にチャネルで渡される実際のデータを気にせず、シグナルのみを気にする場合に、ゴルーチン間のシグナリングによく使用されます。たとえば、空の構造体チャネルを使用して、ゴルーチンに作業を停止するように通知できます。
package main import ( "fmt" "time" ) func main() { quit := make(chan struct{}) go func() { // Simulate work fmt.Println("Working...") time.Sleep(3 * time.Second) // Send quit signal close(quit) }() // Block and wait for quit signal to close <-quit fmt.Println("Quit signal received, exiting...") }
この例では、quit
チャネルが作成され、別のゴルーチンが何らかの作業をシミュレートします。作業が完了すると、quit
チャネルを閉じて終了を通知します。メイン関数は、シグナルを受信するまで<-quit
でブロックし、メッセージを出力して終了します。
チャネルは空の構造体型を使用するため、追加のメモリオーバーヘッドは発生しません。
Goの標準ライブラリでは、context
パッケージのContext
インターフェースには、操作の完了ステータスを通知するために使用されるチャネルを返すDone()
メソッドがあります。この返されたチャネルは、型として空の構造体を使用します。
type Context interface { Deadline() (deadline time.Time, ok bool) Done() <-chan struct{} Err() error Value(key any) any }
メソッドレシーバーとして使用
場合によっては、メソッドのグループ(通常はインターフェースを実装するため)を定義する必要があるが、実装にデータを保存する必要がない場合があります。このような場合、空の構造体を使用できます。
type Person interface { SayHello() Sleep() } type LPC struct{} func (c LPC) SayHello() { fmt.Println("[Leapcell] Hello") } func (c LPC) Sleep() { fmt.Println("[Leapcell] Sleeping...") }
この例では、インターフェースPerson
と構造体LPC
を定義し、メソッドSayHello
とSleep
を使用してPerson
インターフェースを実装します。
LPC
は空の構造体であるため、メモリオーバーヘッドは追加されません。
まとめ
この記事では、まずGo言語の空の構造体の概念と定義を紹介しました。これは2つの方法で定義できます。
次に、メモリ使用量がゼロであることや、この型の複数の変数が同じアドレスを共有するなど、空の構造体の特性について説明しました。
次に、ソースコードをより深く掘り下げ、Goの空の構造体がゼロメモリを持ち、同じアドレスを持つ理由を調査しました。その理由は、割り当てるオブジェクトのサイズ(size
)が0の場合、Goはzerobase
へのポインタを返すためです。
最後に、空の構造体の3つのユースケースをリストし、次のものを含む、いくつかの一般的な実際のシナリオをコード例で示しました。
map[K]struct{}
を使用してSetを実装する- チャネルでシグナリングに空の構造体を使用する
- データストレージが不要な場合に、レシーバーとして空の構造体を使用する
Goプロジェクトのホスティングには、Leapcellが最適です。
Leapcellは、Webホスティング、非同期タスク、およびRedis向けの次世代サーバーレスプラットフォームです。
マルチ言語サポート
- Node.js、Python、Go、またはRustで開発します。
無制限のプロジェクトを無料でデプロイ
- 使用量に対してのみ支払い—リクエストなし、料金なし。
比類のない費用対効果
- アイドル料金なしの従量課金。
- 例:$25で、平均応答時間60msで694万リクエストをサポートします。
合理化された開発者エクスペリエンス
- 簡単なセットアップのための直感的なUI。
- 完全に自動化されたCI/CDパイプラインとGitOps統合。
- 実用的な洞察のためのリアルタイムメトリクスとロギング。
簡単なスケーラビリティと高性能
- 高い同時実行性を簡単に処理するための自動スケーリング。
- 運用オーバーヘッドはゼロ—構築に集中するだけです。
ドキュメントで詳細をご覧ください!
Xでフォローしてください:@LeapcellHQ