Gob
ライブラリ
Gob - Go言語のバイナリシリアライゼーションフォーマット
概要
Gobは、Go言語の標準ライブラリに含まれるバイナリシリアライゼーションフォーマットです。Go言語間でのデータ交換に特化して設計されており、型情報を含む自己記述的なフォーマットを採用しています。encoding/gobパッケージとして提供され、複雑なデータ構造(ポインタ、スライス、マップ、インターフェースなど)を効率的にシリアライズできます。特にRPCやネットワーク通信での使用を想定して最適化されています。
詳細
Gobは、Rob PikeとKen Thompsonによって設計された、Go言語専用のバイナリエンコーディング形式です。Protocol BuffersやMessagePackとは異なり、スキーマ定義を必要とせず、Go言語の型システムと密接に統合されています。データストリームに型情報を埋め込むことで、送信側と受信側で完全に同じ型定義を持つ必要がありません。
Gobの特徴的な設計として、ストリーミング対応があります。単一の接続で複数の値を順次送受信でき、型情報は最初の送信時のみ含まれるため、同じ型の複数の値を効率的に転送できます。また、フィールドの追加や削除に対してある程度の互換性を持ち、構造体の進化に対応できます。
エンコーディングは再帰的に行われ、ポインタの循環参照も適切に処理されます。また、インターフェース型の値もサポートしており、実際の型情報と共にシリアライズされます。ただし、関数、チャネル、unsafe.Pointerなど一部の型はシリアライズできません。
メリット・デメリット
メリット
- Go言語との完全な統合: 標準ライブラリで提供され、追加依存なし
- 自己記述的: スキーマ定義不要で、型情報を自動的に含む
- ストリーミング対応: 複数の値を効率的に送受信可能
- 型安全: コンパイル時の型チェックと実行時の型検証
- ポインタ・循環参照対応: 複雑なデータ構造も正確にシリアライズ
- バージョン互換性: フィールドの追加・削除にある程度対応
デメリット
- Go言語専用: 他の言語との相互運用性なし
- サイズ効率: 型情報を含むため、純粋なバイナリより大きい
- パフォーマンス: JSONより高速だが、特化型シリアライザより低速
- カスタマイズ性の制限: エンコーディング形式の変更不可
- 一部型の非対応: 関数、チャネル等はシリアライズ不可
- デバッグ困難: バイナリフォーマットのため人間が読めない
参考ページ
- encoding/gob - Go標準ライブラリ
- Gob: Go言語のデータ交換
- The Go Programming Language Specification
- Effective Go - Data
書き方の例
1. 基本的なエンコード・デコード
package main
import (
"bytes"
"encoding/gob"
"fmt"
"log"
)
type Person struct {
Name string
Age int
Email string
Active bool
}
func main() {
// エンコード
var buf bytes.Buffer
encoder := gob.NewEncoder(&buf)
person := Person{
Name: "田中太郎",
Age: 30,
Email: "[email protected]",
Active: true,
}
err := encoder.Encode(person)
if err != nil {
log.Fatal("エンコードエラー:", err)
}
// デコード
decoder := gob.NewDecoder(&buf)
var decodedPerson Person
err = decoder.Decode(&decodedPerson)
if err != nil {
log.Fatal("デコードエラー:", err)
}
fmt.Printf("デコード結果: %+v\n", decodedPerson)
}
2. 複数の値のストリーミング
package main
import (
"bytes"
"encoding/gob"
"fmt"
"log"
)
type Message struct {
ID int
Content string
Timestamp int64
}
func streamingExample() {
var network bytes.Buffer // ネットワーク接続をシミュレート
encoder := gob.NewEncoder(&network)
decoder := gob.NewDecoder(&network)
// 送信側: 複数のメッセージを送信
messages := []Message{
{ID: 1, Content: "最初のメッセージ", Timestamp: 1234567890},
{ID: 2, Content: "2番目のメッセージ", Timestamp: 1234567891},
{ID: 3, Content: "3番目のメッセージ", Timestamp: 1234567892},
}
for _, msg := range messages {
if err := encoder.Encode(msg); err != nil {
log.Fatal("送信エラー:", err)
}
}
// 受信側: ストリームから読み取り
for i := 0; i < len(messages); i++ {
var received Message
if err := decoder.Decode(&received); err != nil {
log.Fatal("受信エラー:", err)
}
fmt.Printf("受信: %+v\n", received)
}
}
3. インターフェース型のシリアライゼーション
package main
import (
"bytes"
"encoding/gob"
"fmt"
)
// インターフェース定義
type Animal interface {
Speak() string
}
type Dog struct {
Name string
Breed string
}
func (d Dog) Speak() string {
return fmt.Sprintf("%s: ワンワン!", d.Name)
}
type Cat struct {
Name string
Color string
}
func (c Cat) Speak() string {
return fmt.Sprintf("%s: ニャー!", c.Name)
}
type Zoo struct {
Animals []Animal
}
func interfaceExample() {
// 型を登録(インターフェース使用時は必須)
gob.Register(Dog{})
gob.Register(Cat{})
zoo := Zoo{
Animals: []Animal{
Dog{Name: "ポチ", Breed: "柴犬"},
Cat{Name: "ミケ", Color: "三毛"},
Dog{Name: "ハチ", Breed: "秋田犬"},
},
}
// エンコード
var buf bytes.Buffer
encoder := gob.NewEncoder(&buf)
if err := encoder.Encode(zoo); err != nil {
panic(err)
}
// デコード
var decodedZoo Zoo
decoder := gob.NewDecoder(&buf)
if err := decoder.Decode(&decodedZoo); err != nil {
panic(err)
}
// インターフェースメソッドの呼び出し
for _, animal := range decodedZoo.Animals {
fmt.Println(animal.Speak())
}
}
4. カスタムエンコーディング(GobEncoder/GobDecoder)
package main
import (
"bytes"
"encoding/gob"
"fmt"
"time"
)
// カスタム型
type CustomTime struct {
time.Time
}
// GobEncoderインターフェースを実装
func (ct CustomTime) GobEncode() ([]byte, error) {
return []byte(ct.Format(time.RFC3339)), nil
}
// GobDecoderインターフェースを実装
func (ct *CustomTime) GobDecode(data []byte) error {
t, err := time.Parse(time.RFC3339, string(data))
if err != nil {
return err
}
ct.Time = t
return nil
}
type Event struct {
Name string
Timestamp CustomTime
}
func customEncodingExample() {
event := Event{
Name: "システム起動",
Timestamp: CustomTime{time.Now()},
}
var buf bytes.Buffer
encoder := gob.NewEncoder(&buf)
decoder := gob.NewDecoder(&buf)
// エンコード(カスタムエンコーディングが使用される)
if err := encoder.Encode(event); err != nil {
panic(err)
}
var decoded Event
if err := decoder.Decode(&decoded); err != nil {
panic(err)
}
fmt.Printf("イベント: %s, 時刻: %s\n",
decoded.Name, decoded.Timestamp.Format("2006-01-02 15:04:05"))
}
5. 構造体の進化とバージョン互換性
package main
import (
"bytes"
"encoding/gob"
"fmt"
)
// バージョン1の構造体
type PersonV1 struct {
Name string
Age int
}
// バージョン2の構造体(フィールド追加)
type PersonV2 struct {
Name string
Age int
Email string // 新しいフィールド
Phone string // 新しいフィールド
}
func versionCompatibility() {
// V1でエンコード
v1 := PersonV1{Name: "山田花子", Age: 25}
var buf bytes.Buffer
encoder := gob.NewEncoder(&buf)
encoder.Encode(v1)
// V2でデコード(新しいフィールドはゼロ値)
var v2 PersonV2
decoder := gob.NewDecoder(&buf)
decoder.Decode(&v2)
fmt.Printf("V1→V2: %+v\n", v2)
// V2でエンコード
buf.Reset()
v2Full := PersonV2{
Name: "鈴木一郎",
Age: 30,
Email: "[email protected]",
Phone: "090-1234-5678",
}
encoder = gob.NewEncoder(&buf)
encoder.Encode(v2Full)
// V1でデコード(余分なフィールドは無視)
var v1Decoded PersonV1
decoder = gob.NewDecoder(&buf)
decoder.Decode(&v1Decoded)
fmt.Printf("V2→V1: %+v\n", v1Decoded)
}
6. ファイルへの保存と読み込み
package main
import (
"encoding/gob"
"fmt"
"os"
)
type GameState struct {
Level int
Score int64
PlayerName string
Inventory map[string]int
Position struct {
X, Y, Z float64
}
}
func saveToFile(filename string, state GameState) error {
file, err := os.Create(filename)
if err != nil {
return err
}
defer file.Close()
encoder := gob.NewEncoder(file)
return encoder.Encode(state)
}
func loadFromFile(filename string) (GameState, error) {
var state GameState
file, err := os.Open(filename)
if err != nil {
return state, err
}
defer file.Close()
decoder := gob.NewDecoder(file)
err = decoder.Decode(&state)
return state, err
}
func fileExample() {
gameState := GameState{
Level: 5,
Score: 12500,
PlayerName: "プレイヤー1",
Inventory: map[string]int{
"ポーション": 3,
"エリクサー": 1,
"鍵": 5,
},
Position: struct{ X, Y, Z float64 }{100.5, 50.0, -25.3},
}
// 保存
if err := saveToFile("game.gob", gameState); err != nil {
panic(err)
}
// 読み込み
loaded, err := loadFromFile("game.gob")
if err != nil {
panic(err)
}
fmt.Printf("読み込んだゲーム状態: %+v\n", loaded)
}