Gob
Library
Gob - Go Language Binary Serialization Format
Overview
Gob is a binary serialization format included in Go's standard library. Designed specifically for data exchange between Go programs, it adopts a self-describing format that includes type information. Provided as the encoding/gob package, it can efficiently serialize complex data structures (pointers, slices, maps, interfaces, etc.). It is particularly optimized for use in RPC and network communication scenarios.
Details
Gob is a binary encoding format designed by Rob Pike and Ken Thompson specifically for the Go language. Unlike Protocol Buffers or MessagePack, it doesn't require schema definitions and is tightly integrated with Go's type system. By embedding type information in the data stream, the sender and receiver don't need to have exactly the same type definitions.
A distinctive design feature of Gob is its streaming support. Multiple values can be sent and received sequentially over a single connection, with type information included only on the first transmission, making it efficient to transfer multiple values of the same type. It also has some compatibility with field additions and deletions, supporting struct evolution.
Encoding is performed recursively, and pointer circular references are handled appropriately. It also supports interface type values, which are serialized along with their actual type information. However, some types like functions, channels, and unsafe.Pointer cannot be serialized.
Advantages and Disadvantages
Advantages
- Complete Go Integration: Provided in standard library with no additional dependencies
- Self-Describing: No schema definition required, automatically includes type information
- Streaming Support: Efficiently send and receive multiple values
- Type Safety: Compile-time type checking and runtime type validation
- Pointer & Circular Reference Support: Correctly serializes complex data structures
- Version Compatibility: Some support for field additions and deletions
Disadvantages
- Go-Only: No interoperability with other languages
- Size Efficiency: Larger than pure binary due to type information
- Performance: Faster than JSON but slower than specialized serializers
- Limited Customization: Cannot modify encoding format
- Type Restrictions: Functions, channels, etc. cannot be serialized
- Debugging Difficulty: Binary format is not human-readable
References
- encoding/gob - Go Standard Library
- Gob: Data Exchange in Go
- The Go Programming Language Specification
- Effective Go - Data
Code Examples
1. Basic Encode and Decode
package main
import (
"bytes"
"encoding/gob"
"fmt"
"log"
)
type Person struct {
Name string
Age int
Email string
Active bool
}
func main() {
// Encode
var buf bytes.Buffer
encoder := gob.NewEncoder(&buf)
person := Person{
Name: "John Doe",
Age: 30,
Email: "[email protected]",
Active: true,
}
err := encoder.Encode(person)
if err != nil {
log.Fatal("Encode error:", err)
}
// Decode
decoder := gob.NewDecoder(&buf)
var decodedPerson Person
err = decoder.Decode(&decodedPerson)
if err != nil {
log.Fatal("Decode error:", err)
}
fmt.Printf("Decoded: %+v\n", decodedPerson)
}
2. Streaming Multiple Values
package main
import (
"bytes"
"encoding/gob"
"fmt"
"log"
)
type Message struct {
ID int
Content string
Timestamp int64
}
func streamingExample() {
var network bytes.Buffer // Simulate network connection
encoder := gob.NewEncoder(&network)
decoder := gob.NewDecoder(&network)
// Sender: Send multiple messages
messages := []Message{
{ID: 1, Content: "First message", Timestamp: 1234567890},
{ID: 2, Content: "Second message", Timestamp: 1234567891},
{ID: 3, Content: "Third message", Timestamp: 1234567892},
}
for _, msg := range messages {
if err := encoder.Encode(msg); err != nil {
log.Fatal("Send error:", err)
}
}
// Receiver: Read from stream
for i := 0; i < len(messages); i++ {
var received Message
if err := decoder.Decode(&received); err != nil {
log.Fatal("Receive error:", err)
}
fmt.Printf("Received: %+v\n", received)
}
}
3. Interface Type Serialization
package main
import (
"bytes"
"encoding/gob"
"fmt"
)
// Interface definition
type Animal interface {
Speak() string
}
type Dog struct {
Name string
Breed string
}
func (d Dog) Speak() string {
return fmt.Sprintf("%s: Woof!", d.Name)
}
type Cat struct {
Name string
Color string
}
func (c Cat) Speak() string {
return fmt.Sprintf("%s: Meow!", c.Name)
}
type Zoo struct {
Animals []Animal
}
func interfaceExample() {
// Register types (required for interfaces)
gob.Register(Dog{})
gob.Register(Cat{})
zoo := Zoo{
Animals: []Animal{
Dog{Name: "Buddy", Breed: "Golden Retriever"},
Cat{Name: "Whiskers", Color: "Orange"},
Dog{Name: "Max", Breed: "German Shepherd"},
},
}
// Encode
var buf bytes.Buffer
encoder := gob.NewEncoder(&buf)
if err := encoder.Encode(zoo); err != nil {
panic(err)
}
// Decode
var decodedZoo Zoo
decoder := gob.NewDecoder(&buf)
if err := decoder.Decode(&decodedZoo); err != nil {
panic(err)
}
// Call interface methods
for _, animal := range decodedZoo.Animals {
fmt.Println(animal.Speak())
}
}
4. Custom Encoding (GobEncoder/GobDecoder)
package main
import (
"bytes"
"encoding/gob"
"fmt"
"time"
)
// Custom type
type CustomTime struct {
time.Time
}
// Implement GobEncoder interface
func (ct CustomTime) GobEncode() ([]byte, error) {
return []byte(ct.Format(time.RFC3339)), nil
}
// Implement GobDecoder interface
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: "System startup",
Timestamp: CustomTime{time.Now()},
}
var buf bytes.Buffer
encoder := gob.NewEncoder(&buf)
decoder := gob.NewDecoder(&buf)
// Encode (custom encoding is used)
if err := encoder.Encode(event); err != nil {
panic(err)
}
var decoded Event
if err := decoder.Decode(&decoded); err != nil {
panic(err)
}
fmt.Printf("Event: %s, Time: %s\n",
decoded.Name, decoded.Timestamp.Format("2006-01-02 15:04:05"))
}
5. Struct Evolution and Version Compatibility
package main
import (
"bytes"
"encoding/gob"
"fmt"
)
// Version 1 struct
type PersonV1 struct {
Name string
Age int
}
// Version 2 struct (fields added)
type PersonV2 struct {
Name string
Age int
Email string // New field
Phone string // New field
}
func versionCompatibility() {
// Encode with V1
v1 := PersonV1{Name: "Alice Smith", Age: 25}
var buf bytes.Buffer
encoder := gob.NewEncoder(&buf)
encoder.Encode(v1)
// Decode with V2 (new fields get zero values)
var v2 PersonV2
decoder := gob.NewDecoder(&buf)
decoder.Decode(&v2)
fmt.Printf("V1→V2: %+v\n", v2)
// Encode with V2
buf.Reset()
v2Full := PersonV2{
Name: "Bob Johnson",
Age: 30,
Email: "[email protected]",
Phone: "555-1234",
}
encoder = gob.NewEncoder(&buf)
encoder.Encode(v2Full)
// Decode with V1 (extra fields are ignored)
var v1Decoded PersonV1
decoder = gob.NewDecoder(&buf)
decoder.Decode(&v1Decoded)
fmt.Printf("V2→V1: %+v\n", v1Decoded)
}
6. File Save and Load
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: "Player1",
Inventory: map[string]int{
"Potion": 3,
"Elixir": 1,
"Key": 5,
},
Position: struct{ X, Y, Z float64 }{100.5, 50.0, -25.3},
}
// Save
if err := saveToFile("game.gob", gameState); err != nil {
panic(err)
}
// Load
loaded, err := loadFromFile("game.gob")
if err != nil {
panic(err)
}
fmt.Printf("Loaded game state: %+v\n", loaded)
}