[インデックス 12655] ファイルの概要
このコミットは、Go言語のencoding/gob
パッケージに関する詳細な解説記事「Gobs of data」を公式ドキュメントに追加するものです。この記事は、Go言語のブログで2011年3月24日に公開されたものをドキュメントとして取り込むことで、gob
パッケージの設計思想、機能、および使用方法について、より永続的かつアクセスしやすい形で情報を提供することを目的としています。
コミット
commit 9e03dcb3fa3a024330909808d7338f0d194bf05a
Author: Francisco Souza <franciscossouza@gmail.com>
Date: Fri Mar 16 08:21:13 2012 +1100
doc: add Gobs of data article
Originally published on The Go Programming Language Blog, March 24, 2011.
http://blog.golang.org/2011/03/gobs-of-data.html
R=adg
CC=golang-dev
https://golang.org/cl/5834043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/9e03dcb3fa3a024330909808d7338f0d194bf05a
元コミット内容
doc: add Gobs of data article
Originally published on The Go Programming Language Blog, March 24, 2011.
http://blog.golang.org/2011/03/gobs-of-data.html
R=adg
CC=golang-dev
https://golang.org/cl/5834043
変更の背景
このコミットの背景には、Go言語の標準ライブラリであるencoding/gob
パッケージの重要性と、その設計思想をコミュニティに広く理解してもらう必要がありました。gob
はGo言語に特化したデータシリアライゼーションフォーマットであり、Goプログラム間での効率的なデータ交換や永続化に利用されます。
元々、この解説はGo公式ブログで公開されていましたが、ブログ記事は時間の経過とともに見つけにくくなる可能性があります。公式ドキュメントに組み込むことで、gob
パッケージの利用を検討する開発者が、その設計意図、利点、および具体的な使用例を容易に参照できるようになります。これにより、gob
の適切な利用が促進され、Goエコシステムの健全な発展に寄与することが期待されます。特に、net/rpc
パッケージがgob
の上に構築されていることからも、その基礎となるgob
の理解は非常に重要です。
前提知識の解説
データシリアライゼーションとは
データシリアライゼーション(またはマーシャリング、直列化)とは、データ構造やオブジェクトの状態を、ファイルへの保存やネットワークを介した転送が可能な形式に変換するプロセスです。これにより、異なるシステム間でのデータ交換や、プログラムの実行をまたいだデータの永続化が可能になります。
既存のデータエンコーディング形式
世の中には様々なデータエンコーディング形式が存在し、それぞれ異なる特性と用途を持っています。
- JSON (JavaScript Object Notation): 人間が読み書きしやすく、軽量なデータ交換フォーマットです。Web APIなどで広く利用されています。テキストベースであるため、バイナリデータに比べてサイズが大きくなりがちで、パース速度も遅い傾向があります。
- XML (Extensible Markup Language): 構造化されたデータを表現するための汎用的なマークアップ言語です。複雑なデータ構造やドキュメントの表現に適していますが、冗長性が高く、パースにコストがかかることがあります。
- Protocol Buffers (Protobuf): Googleが開発した、構造化データをシリアライズするための言語ニュートラル、プラットフォームニュートラル、拡張可能なメカニズムです。スキーマ定義言語(.protoファイル)を用いてデータ構造を定義し、それに基づいてコードを生成します。バイナリ形式で効率的ですが、スキーマ定義が必須であり、自己記述性はありません。
- Gob (Go Binary): Go言語に特化したバイナリエンコーディング形式です。Goの型システムと密接に連携し、リフレクションを活用してデータ構造を自動的にエンコード・デコードします。
encoding/gob
パッケージ
encoding/gob
パッケージは、Go言語の標準ライブラリの一部であり、Goのデータ構造をバイナリ形式でエンコード(シリアライズ)およびデコード(デシリアライズ)するための機能を提供します。主にGoプログラム間での通信(例: net/rpc
)や、Goプログラムが生成したデータの永続化に利用されます。
gob
の主な特徴は以下の通りです。
- Go言語に特化: Goの型システムとリフレクションを最大限に活用します。これにより、スキーマ定義ファイルが不要で、Goの構造体を直接エンコード・デコードできます。
- 自己記述性:
gob
ストリームは、データだけでなく、そのデータの型情報も含まれています。これにより、受信側は事前に型を知らなくてもデータをデコードできます。これは、時間の経過とともにデータ構造が変化しても、古いデータをデコードできる柔軟性を提供します。 - 効率性: バイナリ形式であるため、JSONやXMLのようなテキストベースの形式に比べて、データサイズが小さく、エンコード・デコードが高速です。
- 柔軟な型変換: エンコード時とデコード時で型が完全に一致していなくても、フィールド名と互換性のある型に基づいてデータをマッピングします。これにより、データ構造の進化に対応しやすくなっています。
技術的詳細
「Gobs of data」記事では、gob
パッケージの設計目標と、他のシリアライゼーション形式(特にProtocol Buffers)との比較を通じて、その技術的な詳細が解説されています。
gob
の設計目標
- 使いやすさ: Goのリフレクションを活用することで、別途インターフェース定義言語や「プロトコルコンパイラ」が不要です。データ構造そのものがエンコード・デコード方法を決定します。これは
gob
がGoに特化しているからこそ可能であり、他の言語との互換性よりもGo内での使いやすさを優先しています。 - 効率性: XMLやJSONのようなテキスト表現は、効率的な通信ネットワークの中心に置くには遅すぎると考えられています。そのため、
gob
はバイナリエンコーディングを採用しています。 - 自己記述性: 各
gob
ストリームは、その内容について事前に何も知らないエージェントでも全体をパースできる十分な情報を含んでいます。これにより、ファイルに保存されたgob
ストリームを、そのデータが何を表しているかを忘れてしまった後でもデコードできるという特性があります。
Protocol Buffersとの比較とgob
の設計選択
記事では、Protocol Buffersのいくつかの「誤った特徴」を挙げ、gob
がそれらをどのように避けて設計されたかを説明しています。
- トップレベルでの型制限: Protocol Buffersは、トップレベルで整数や配列をエンコードできず、
struct
型のみを扱います。gob
はこのような制限を設けず、任意のGoの型をトップレベルでエンコードできます。 - 必須フィールド (Required Fields): Protocol Buffersの必須フィールドは、実装コストが高く、データ定義の変更時に既存クライアントのクラッシュを引き起こす可能性があるため、
gob
では採用されていません。gob
では、すべてのフィールドが実質的にオプションとして扱われます。 - デフォルト値 (Default Values): Protocol Buffersのデフォルト値は、実装が複雑であり、Goの「ゼロ値」の概念と相性が悪いため、
gob
では採用されていません。gob
では、フィールドが送信されない場合、受信側ではその型のゼロ値が設定されます。
gob
における値の扱い
- 抽象的な整数値:
gob
は、int8
やuint16
のような具体的なサイズではなく、抽象的な「サイズなし」の整数値としてデータを扱います。これにより、エンコードされた値は、受信側で任意の整数型にデコードできる柔軟性があります。例えば、int8
から送信された7
は、受信側でint64
に格納できます。 - ポインタの平坦化:
gob
はポインタを平坦化して扱います。int8
、*int8
、**int8
などの型はすべて整数値として送信され、受信側で任意のポインタ深度を持つ整数型に格納できます。 - 構造体の柔軟なデコード: 構造体をデコードする際、エンコーダが送信したフィールドのみが受信側の構造体に格納されます。フィールドは名前と互換性のある型によってマッチングされ、両方に存在するフィールドのみが影響を受けます。これにより、型が進化してフィールドが追加されても、古い受信側は認識できる部分で機能し続けることができます。これは「オプションフィールド」の問題を解決するアプローチです。
- 浮動小数点数: 浮動小数点数はIEEE 754のビットパターンとして整数で表現され、送信されます。バイトオーダーは、一般的な値でゼロが多くなる下位ビットを省略するためにバイト反転されます。
- カスタムエンコーディング:
GobEncoder
およびGobDecoder
インターフェースを実装することで、ユーザーは独自の型に対してカスタムのエンコード・デコードロジックを定義できます。これはjson.Marshaler
やfmt.Stringer
と同様のメカニズムです。
gob
における型の伝送
gob
ストリームでは、特定の型が初めて送信される際に、その型の記述がデータストリームに含まれます。エンコーダは、内部的な型記述構造体を標準のgob
エンコーディング形式でエンコードし、それにユニークな型番号を割り当てます。一度型が記述されると、それ以降はその型番号で参照されます。
これにより、gob
ストリームは完全に自己記述的になります(ブートストラップ型を除く)。再帰的な型(例: ツリー構造)もエンコード可能です。
内部的な最適化: マシンのコンパイル
gob
パッケージは、特定の型の値を初めてエンコードする際に、そのデータ型に特化した「解釈型マシン」を構築します。このマシンはリフレクションを使用して構築されますが、一度構築されるとリフレクションに依存せず、unsafe
パッケージとトリックを用いて高速にデータをエンコードします。デコードも同様に、エンコーダが定義した型とデコード先のGoの型のペアに対してデコードマシンを構築し、unsafe
メソッドを使用して最大速度を実現します。これにより、同じ型の後続の値は、すでにコンパイルされたマシンを使用してすぐにエンコード・デコードできます。
コアとなるコードの変更箇所
このコミットによる主な変更ファイルは以下の通りです。
doc/Makefile
: 新しい記事articles/gobs_of_data.rawhtml
をビルド対象に追加。doc/articles/gobs_of_data.html
: 「Gobs of data」記事のHTMLコンテンツ。これがこのコミットの主要な追加ファイルです。doc/docs.html
: Goのドキュメントインデックスページに、新しい記事へのリンクを追加。doc/progs/gobs1.go
: 記事内で参照されるGoのサンプルコード。構造体の柔軟なデコードや再帰的な型の例を示しています。doc/progs/gobs2.go
: 記事内で参照されるGoのサンプルコード。gob
エンコーダとデコーダの基本的な使用例を示しています。doc/progs/run
: ドキュメント内のサンプルプログラムを実行するためのスクリプトに、gobs1
とgobs2
を追加。src/pkg/encoding/gob/doc.go
:encoding/gob
パッケージのドキュメントコメント内のブログ記事へのリンクを、新しいドキュメントのパスに変更。
コアとなるコードの解説
doc/articles/gobs_of_data.html
このファイルは、gob
パッケージに関する詳細な解説記事の本体です。gob
の設計目標、Protocol Buffersとの比較、値のエンコード方法、型の伝送メカニズム、内部的な最適化(マシンのコンパイル)、そして具体的な使用例が記述されています。記事内では、doc/progs/gobs1.go
とdoc/progs/gobs2.go
のコードスニペットが埋め込まれており、概念を具体的なコードで説明しています。
doc/progs/gobs1.go
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gobs1
type T struct{ X, Y, Z int } // Only exported fields are encoded and decoded.
var t = T{X: 7, Y: 0, Z: 8}
// STOP OMIT
type U struct{ X, Y *int8 } // Note: pointers to int8s
var u U
// STOP OMIT
type Node struct {
Value int
Left, Right *Node
}
// STOP OMIT
このファイルは、gob
記事内で例として使用されるGoのコードスニペットを含んでいます。
type T struct{ X, Y, Z int }
:gob
がエクスポートされたフィールドのみをエンコード・デコードすること、およびゼロ値のフィールド(Y: 0
)が送信されないことを示す例です。type U struct{ X, Y *int8 }
: ポインタ型がどのように扱われるか、特にint8
へのポインタがgob
によってどのように平坦化されるかを示す例です。type Node struct { ... }
: 再帰的なデータ構造(ツリー)がgob
でどのようにエンコードできるかを示す例です。
これらのスニペットは、gob
の柔軟な型変換と、ポインタや再帰型に対する扱いを説明するために用いられています。
doc/progs/gobs2.go
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"encoding/gob"
"fmt"
"log"
)
type P struct {
X, Y, Z int
Name string
}
type Q struct {
X, Y *int32
Name string
}
func main() {
// Initialize the encoder and decoder. Normally enc and dec would be
// bound to network connections and the encoder and decoder would
// run in different processes.
var network bytes.Buffer // Stand-in for a network connection
enc := gob.NewEncoder(&network) // Will write to network.
dec := gob.NewDecoder(&network) // Will read from network.
// Encode (send) the value.
err := enc.Encode(P{3, 4, 5, "Pythagoras"})
if err != nil {
log.Fatal("encode error:", err)
}
// Decode (receive) the value.
var q Q
err = dec.Decode(&q)
if err != nil {
log.Fatal("decode error:", err)
}
fmt.Printf("%q: {%d,%d}\n", q.Name, *q.X, *q.Y)
}
このファイルは、gob
パッケージの基本的な使用方法を示す完全な実行可能な例です。
bytes.Buffer
をネットワーク接続の代わりとして使用し、gob.NewEncoder
とgob.NewDecoder
を初期化しています。P
という構造体をエンコードし、その値をQ
という異なる構造体にデコードしています。ここで注目すべきは、P
のフィールドX, Y, Z
がint
型であるのに対し、Q
のフィールドX, Y
は*int32
型である点です。gob
はこれらの型の違いを吸収し、フィールド名に基づいて適切に値をマッピングし、ポインタのデリファレンスも行います。- この例は、
gob
が異なるが互換性のある型間でのデータ交換をいかに容易にするかを示しています。
関連リンク
- Go Programming Language Blog: Gobs of data (元のブログ記事)
- Go
encoding/gob
パッケージドキュメント: https://pkg.go.dev/encoding/gob - Go
net/rpc
パッケージドキュメント: https://pkg.go.dev/net/rpc (gobの上に構築されています)
参考にした情報源リンク
- http://blog.golang.org/2011/03/gobs-of-data.html
- コミットメッセージ内の変更ファイルリストと内容
- Go言語の公式ドキュメント (
encoding/gob
パッケージ)