Go Opaque API: 手動マイグレーション
Opaque API は、Go プログラミング言語向けの Protocol Buffers 実装の最新バージョンです。古いバージョンは現在 Open Struct API と呼ばれています。概要については、Go Protobuf: Releasing the Opaque API のブログ記事をご覧ください。
これは、古い Open Struct API から新しい Opaque API へ Go Protobuf の利用箇所をマイグレーションするためのユーザーガイドです。
警告
現在、手動マイグレーションガイドをご覧になっています。通常、マイグレーションを自動化するにはopen2opaque
ツールを使用する方が良いでしょう。代わりに 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: ビルダーとセッターのどちらを使用すべきですか? を参照してください。
上記の例の例外は、oneof を扱う場合です。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
を呼び出すと、ゼロ長の slice (nil または空の slice のいずれか) が返されます。ユーザーは、存在を確認するために 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;
}
Repeated フィールドには、Get
および Set
メソッドがあります。
Get
はフィールドの値を返します。フィールドが設定されていない場合、またはメッセージレシーバーが nil の場合は nil を返します。
Set
は、指定された値をフィールドに格納します。nil メッセージレシーバーで呼び出されるとパニックになります。Set
は、指定されたスライスヘッダーのコピーを格納します。スライスの内容の変更は、repeated フィールドで確認できます。したがって、Set
が空のスライスで呼び出された場合、直後に Get
を呼び出すと、同じスライスが返されます。ワイヤーまたはテキストのマーシャリング出力の場合、渡された nil スライスは空のスライスと区別できません。
メッセージ Concert
の support_acts
という名前の repeated メッセージ型フィールドの場合、次のアクセサーメソッドが生成されます。
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 (新) |
|
|
Oneofs
各 oneof ユニオングループ化に対して、メッセージに Which
、Has
、および Clear
メソッドがあります。また、そのユニオンの各 oneof ケースフィールドには、Get
、Set
、Has
、および Clear
メソッドもあります。
たとえば、次のように oneof avatar
に oneof フィールド image_url
と image_data
が定義されたメッセージがあるとします。
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 ユニオン内で以前に設定されていたケースフィールドを暗黙的にクリアします。nil 値で oneof メッセージケースフィールドで Set
を呼び出すと、フィールドは空のメッセージに設定されます。nil メッセージレシーバーで呼び出されるとパニックになります。
Has
は、ケースフィールドが設定されているかどうかを報告します。nil メッセージレシーバーで呼び出されると false を返します。
Clear
はケースフィールドをクリアします。以前に設定されていた場合、oneof ユニオンもクリアされます。oneof ユニオンが別のフィールドに設定されている場合、oneof ユニオンはクリアされません。nil メッセージレシーバーで呼び出されるとパニックになります。
コードスニペットの例:
Open Struct API (旧) | Opaque API (新) |
|
|
リフレクション
Open Struct API から移行すると、proto メッセージ型で Go reflect
パッケージを使用して構造体フィールドとタグにアクセスするコードは機能しなくなります。コードは protoreflect を使用するように移行する必要があります。
一部の一般的なライブラリは、内部で Go reflect
を使用しています。例を挙げます。
- encoding/json
- protobuf/encoding/protojson を使用してください。
- pretty
- cmp
- protobuf メッセージで
cmp.Equal
を適切に使用するには、protocmp.Transform を使用してください。
- protobuf メッセージで