Go Opaque API: 手動での移行
Opaque API は、Go プログラミング言語向けの Protocol Buffers 実装の最新バージョンです。以前のバージョンは Open Struct API と呼ばれていました。導入については、Go Protobuf: Releasing the Opaque API のブログ記事をご覧ください。
これは、Go Protobuf の使用を古い Open Struct API から新しい Opaque API に移行するためのユーザーガイドです。
生成されたコードガイドは、より詳細な情報を提供します。このガイドでは、古い API と新しい API を並べて比較します。
メッセージの構築
次のように定義された protobuf メッセージがあるとします。
message Foo {
uint32 uint32 = 1;
bytes bytes = 2;
oneof union {
string string = 4;
MyMessage message = 5;
}
enum Kind { … };
Kind kind = 9;
}
リテラル値からこのメッセージを構築する方法の例を次に示します。
Open Struct API (旧) | Opaque API (新) |
|
|
ご覧のとおり、ビルダー構造体を使用すると、Open Struct API (旧) と Opaque API (新) の間でほぼ 1 対 1 の変換が可能です。
一般的に、可読性のためにビルダーを使用することを推奨します。ホットな内部ループで Protobuf メッセージを作成するようなまれなケースでのみ、ビルダーの代わりにセッターを使用する方が良い場合があります。詳細については、Opaque API FAQ: ビルダーとセッターのどちらを使用すべきですか?をご覧ください。
上記の例の例外は、oneofs を扱う場合です。Open Struct API (旧) は各 oneof ケースにラッパー構造体型を使用するのに対し、Opaque API (新) は oneof フィールドを通常のメッセージフィールドのように扱います。
Open Struct API (旧) | Opaque API (新) |
|
|
oneof ユニオンに関連付けられた Go 構造体フィールドのセットでは、1 つのフィールドのみを格納できます。複数の oneof ケースフィールドが格納されている場合、最後に宣言されたフィールド(.proto ファイル内のフィールド宣言順序)が優先されます。
スカラーフィールド
スカラーフィールドで定義されたメッセージがあるとします。
message Artist {
int32 birth_year = 1;
}
Go がスカラー型 (bool, int32, int64, uint32, uint64, float32, float64, string, []byte, および enum) を使用する Protobuf メッセージフィールドには、Get
および Set
アクセサーメソッドがあります。明示的な存在を持つフィールドには、Has
および Clear
メソッドもあります。
birth_year
という名前の int32
型のフィールドの場合、以下のアクセサーメソッドが生成されます。
func (m *Artist) GetBirthYear() int32
func (m *Artist) SetBirthYear(v int32)
func (m *Artist) HasBirthYear() bool
func (m *Artist) ClearBirthYear()
Get
はフィールドの値を返します。フィールドが設定されていないか、メッセージレシーバが nil の場合、デフォルト値を返します。デフォルト値は、デフォルトオプションで明示的に設定されていない限り、ゼロ値です。
Set
は指定された値をフィールドに格納します。nil メッセージレシーバで呼び出された場合、パニックを起こします。
バイトフィールドの場合、nil []byte を指定して Set
を呼び出すと、設定済みと見なされます。例えば、直後に Has
を呼び出すと true を返します。直後に Get
を呼び出すと、長さゼロのスライス(nil または空のスライスのいずれか)が返されます。ユーザーは、存在を判断するために Has
を使用し、Get
が nil を返すかどうかに依存すべきではありません。
Has
はフィールドが格納されているかどうかを報告します。nil メッセージレシーバで呼び出された場合、false を返します。
Clear
はフィールドをクリアします。nil メッセージレシーバで呼び出された場合、パニックを起こします。
文字列フィールドを使用したコードスニペットの例。
Open Struct API (旧) | Opaque API (新) |
|
|
メッセージフィールド
メッセージ型フィールドで定義されたメッセージがあるとします。
message Band {}
message Concert {
Band headliner = 1;
}
メッセージ型の Protobuf メッセージフィールドには、Get
、Set
、Has
、および Clear
メソッドがあります。
headliner
という名前のメッセージ型フィールドの場合、以下のアクセサーメソッドが生成されます。
func (m *Concert) GetHeadliner() *Band
func (m *Concert) SetHeadliner(*Band)
func (m *Concert) HasHeadliner() bool
func (m *Concert) ClearHeadliner()
Get
はフィールドの値を返します。設定されていない場合、または nil メッセージレシーバで呼び出された場合、nil を返します。Get
が nil を返すかどうかをチェックすることは、Has
が false を返すかどうかをチェックすることと同じです。
Set
は指定された値をフィールドに格納します。nil メッセージレシーバで呼び出された場合、パニックを起こします。nil ポインタで Set
を呼び出すことは、Clear
を呼び出すことと同じです。
Has
はフィールドが格納されているかどうかを報告します。nil メッセージレシーバで呼び出された場合、false を返します。
Clear
はフィールドをクリアします。nil メッセージレシーバで呼び出された場合、パニックを起こします。
コードスニペットの例。
Open Struct API (旧) | Opaque (新) |
|
|
繰り返しフィールド
繰り返しメッセージ型フィールドで定義されたメッセージがあるとします。
message Concert {
repeated Band support_acts = 2;
}
繰り返しフィールドには、Get
と Set
メソッドがあります。
Get
はフィールドの値を返します。フィールドが設定されていないか、メッセージレシーバが nil の場合、nil を返します。
Set
は指定された値をフィールドに格納します。nil メッセージレシーバで呼び出された場合、パニックを起こします。Set
は提供されたスライスヘッダーのコピーを格納します。スライスコンテンツの変更は、繰り返しフィールドで観測可能です。したがって、空のスライスで Set
が呼び出された場合、直後に Get
を呼び出すと、同じスライスが返されます。ワイヤまたはテキストマーシャリング出力の場合、渡された nil スライスは空のスライスと区別できません。
メッセージ Concert
の support_acts
という名前の繰り返しメッセージ型フィールドの場合、以下のアクセサーメソッドが生成されます。
func (m *Concert) GetSupportActs() []*Band
func (m *Concert) SetSupportActs([]*Band)
コードスニペットの例。
Open Struct API (旧) | Opaque API (新) |
|
|
マップ
マップ型フィールドで定義されたメッセージがあるとします。
message MerchBooth {
map<string, MerchItems> items = 1;
}
マップフィールドには、Get
と Set
メソッドがあります。
Get
はフィールドの値を返します。フィールドが設定されていないか、メッセージレシーバが nil の場合、nil を返します。
Set
は指定された値をフィールドに格納します。nil メッセージレシーバで呼び出された場合、パニックを起こします。Set
は提供されたマップ参照のコピーを格納します。提供されたマップへの変更は、マップフィールドで観測可能です。
メッセージ MerchBooth
の items
という名前のマップフィールドの場合、以下のアクセサーメソッドが生成されます。
func (m *MerchBooth) GetItems() map[string]*MerchItem
func (m *MerchBooth) SetItems(map[string]*MerchItem)
コードスニペットの例。
Open Struct API (旧) | Opaque API (新) |
|
|
Oneof
各 oneof ユニオングループには、メッセージに Which
、Has
、および Clear
メソッドがあります。また、そのユニオン内の各 oneof ケースフィールドには、Get
、Set
、Has
、および Clear
メソッドがあります。
avatar
oneof に image_url
および image_data
oneof フィールドを持つメッセージが次のように定義されているとします。
message Profile {
oneof avatar {
string image_url = 1;
bytes image_data = 2;
}
}
この oneof に対して生成される Opaque API は次のようになります。
func (m *Profile) WhichAvatar() case_Profile_Avatar { … }
func (m *Profile) HasAvatar() bool { … }
func (m *Profile) ClearAvatar() { … }
type case_Profile_Avatar protoreflect.FieldNumber
const (
Profile_Avatar_not_set_case case_Profile_Avatar = 0
Profile_ImageUrl_case case_Profile_Avatar = 1
Profile_ImageData_case case_Profile_Avatar = 2
)
Which
は、フィールド番号を返すことで、どのケースフィールドが設定されているかを報告します。何も設定されていない場合、または nil メッセージレシーバで呼び出された場合、0 を返します。
Has
は、oneof 内のいずれかのフィールドが設定されているかどうかを報告します。nil メッセージレシーバで呼び出された場合、false を返します。
Clear
は、oneof で現在設定されているケースフィールドをクリアします。nil メッセージレシーバで呼び出された場合、パニックを起こします。
各 oneof ケースフィールドに対して生成される Opaque API は次のようになります。
func (m *Profile) GetImageUrl() string { … }
func (m *Profile) GetImageData() []byte { … }
func (m *Profile) SetImageUrl(v string) { … }
func (m *Profile) SetImageData(v []byte) { … }
func (m *Profile) HasImageUrl() bool { … }
func (m *Profile) HasImageData() bool { … }
func (m *Profile) ClearImageUrl() { … }
func (m *Profile) ClearImageData() { … }
Get
はケースフィールドの値を返します。ケースフィールドが設定されていない場合、または nil メッセージレシーバで呼び出された場合、ゼロ値を返します。
Set
は指定された値をケースフィールドに格納します。また、以前に oneof ユニオン内で格納されていたケースフィールドも暗黙的にクリアします。oneof メッセージケースフィールドで nil 値を指定して Set
を呼び出すと、フィールドは空のメッセージに設定されます。nil メッセージレシーバで呼び出された場合、パニックを起こします。
Has
は、ケースフィールドが設定されているかどうかを報告します。nil メッセージレシーバで呼び出された場合、false を返します。
Clear
はケースフィールドをクリアします。以前に設定されていた場合、oneof ユニオンもクリアされます。oneof ユニオンが異なるフィールドに設定されている場合、oneof ユニオンはクリアされません。nil メッセージレシーバで呼び出された場合、パニックを起こします。
コードスニペットの例。
Open Struct API (旧) | Opaque API (新) |
|
|
リフレクション
Go の reflect
パッケージを proto メッセージ型で使用して構造体フィールドやタグにアクセスするコードは、Open Struct API から移行すると機能しなくなります。コードは protoreflect を使用するように移行する必要があります。
一般的なライブラリの中には、内部で Go の reflect
を使用しているものがあります。例を次に示します。
- encoding/json
- protobuf/encoding/protojson を使用してください。
- pretty
- cmp
- protobuf メッセージで
cmp.Equal
を適切に使用するには、protocmp.Transform を使用してください。
- protobuf メッセージで