Protobuf Editions の概要
Protobuf Editions は、Protocol Buffers で使用してきた proto2 および proto3 の指定に代わるものです。proto 定義ファイルの先頭に syntax = "proto2"
または syntax = "proto3"
を追加する代わりに、edition = "2023"
などのエディション番号を使用して、ファイルが持つデフォルトの動作を指定します。エディションを使用すると、言語を段階的に進化させることができます。
古いバージョンが持っていたハードコードされた動作の代わりに、エディションは機能ごとのデフォルト値 (動作) を持つ機能のコレクションを表します。機能は、protoc、コード ジェネレーター、および protobuf ランタイムの動作を指定する、ファイル、メッセージ、フィールド、enum などに関するオプションです。選択したエディションのデフォルトの動作がニーズに合わない場合は、これらの異なるレベル (ファイル、メッセージ、フィールド、…) で動作を明示的にオーバーライドできます。オーバーライドをさらにオーバーライドすることもできます。このトピックの後半のレキシカル スコーピングに関するセクションで、その詳細について説明します。
最新リリースされたエディションは 2023 です。
機能のライフサイクル
エディションは、機能のライフサイクルの基本的な増分を提供します。機能には、導入、デフォルトの動作の変更、非推奨、そして削除という予想されるライフサイクルがあります。例:
エディション 2031 は、デフォルト値が
false
のfeature.amazing_new_feature
を作成します。この値は、以前のすべてのエディションと同じ動作を維持します。つまり、影響がないことがデフォルトです。開発者は、.proto ファイルを
edition = "2031"
に更新します。後のエディション (エディション 2033 など) では、
feature.amazing_new_feature
のデフォルトをfalse
からtrue
に切り替えます。これはすべての proto で望ましい動作であり、protobuf チームが機能を作成した理由です。Prototiller ツールを使用して以前のバージョンの proto ファイルをエディション 2033 に移行すると、以前の動作を維持するために必要な場合に明示的な
feature.amazing_new_feature = false
エントリが追加されます。開発者は、新しい動作を .proto ファイルに適用したい場合に、これらの新しく追加された設定を削除します。
ある時点で、
feature.amazing_new_feature
はエディションで非推奨とマークされ、後のエディションで削除されます。機能が削除されると、その動作のコード ジェネレーターと、それをサポートするランタイム ライブラリも削除される可能性があります。ただし、タイムラインは寛大になります。ライフサイクルの前の手順の例に従うと、非推奨はエディション 2034 で発生する可能性がありますが、約 2 年後のエディション 2036 まで削除されない可能性があります。機能を削除すると、常にメジャー バージョンのバンプが開始されます。
このライフサイクルにより、非推奨の機能を使用していない .proto
ファイルは、あるエディションから次のエディションへのnoop アップグレードになります。Google の移行期間と非推奨期間の全期間を利用して、コードをアップグレードできます。
上記のライフサイクルの例では、機能にブール値を使用しましたが、機能では enum も使用できます。たとえば、features.field_presence
には、LEGACY_REQUIRED
、EXPLICIT
、および IMPLICIT
の値があります。
Protobuf Editions への移行
エディションは既存のバイナリを壊すことはなく、メッセージのバイナリ、テキスト、または JSON シリアライズ形式を変更しません。最初のエディションは、可能な限り破壊的でないように設計されています。最初のエディションはベースラインを確立し、proto2 および proto3 の定義を新しい単一の定義形式に結合します。
後続のエディションがリリースされると、機能のデフォルトの動作が変更される場合があります。Prototiller に .proto ファイルの noop 変換を実行させることも、新しい動作の一部またはすべてを受け入れることもできます。エディションは、ほぼ年に一度リリースされる予定です。
Proto2 から Editions へ
このセクションでは、proto2 ファイルと、Prototiller ツールを実行して定義ファイルを Protobuf Editions 構文を使用するように変更した場合の外観を示します。
Proto2 構文
// proto2 file
syntax = "proto2";
package com.example;
message Player {
// in proto2, optional fields have explicit presence
optional string name = 1 [default = "N/A"];
// proto2 still supports the problematic "required" field rule
required int32 id = 2;
// in proto2 this is not packed by default
repeated int32 scores = 3;
enum Handed {
HANDED_UNSPECIFIED = 0;
HANDED_LEFT = 1;
HANDED_RIGHT = 2;
HANDED_AMBIDEXTROUS = 3;
}
// in proto2 enums are closed
optional Handed handed = 4;
reserved "gender";
}
Editions 構文
// Edition version of proto2 file
edition = "2023";
package com.example;
option features.utf8_validation = NONE;
message Player {
// fields have explicit presence, so no explicit setting needed
string name = 1 [default = "N/A"];
// to match the proto2 behavior, LEGACY_REQUIRED is set at the field level
int32 id = 2 [features.field_presence = LEGACY_REQUIRED];
// to match the proto2 behavior, EXPANDED is set at the field level
repeated int32 scores = 3 [features.repeated_field_encoding = EXPANDED];
enum Handed {
// this overrides the default edition 2023 behavior, which is OPEN
option features.enum_type = CLOSED;
HANDED_UNSPECIFIED = 0;
HANDED_LEFT = 1;
HANDED_RIGHT = 2;
HANDED_AMBIDEXTROUS = 3;
}
Handed handed = 4;
reserved gender;
}
Proto3 から Editions へ
このセクションでは、proto3 ファイルと、Prototiller ツールを実行して定義ファイルを Protobuf Editions 構文を使用するように変更した場合の外観を示します。
Proto3 構文
// proto3 file
syntax = "proto3";
package com.example;
message Player {
// in proto3, optional fields have explicit presence
optional string name = 1 [default = "N/A"];
// in proto3 no specified field rule defaults to implicit presence
int32 id = 2;
// in proto3 this is packed by default
repeated int32 scores = 3;
enum Handed {
HANDED_UNSPECIFIED = 0;
HANDED_LEFT = 1;
HANDED_RIGHT = 2;
HANDED_AMBIDEXTROUS = 3;
}
// in proto3 enums are open
optional Handed handed = 4;
reserved "gender";
}
Editions 構文
// Editions version of proto3 file
edition = "2023";
package com.example;
message Player {
// fields have explicit presence, so no explicit setting needed
string name = 1 [default = "N/A"];
// to match the proto3 behavior, IMPLICIT is set at the field level
int32 id = 2 [features.field_presence = IMPLICIT];
// PACKED is the default state, and is provided just for illustration
repeated int32 scores = 3 [features.repeated_field_encoding = PACKED];
enum Handed {
HANDED_UNSPECIFIED = 0;
HANDED_LEFT = 1;
HANDED_RIGHT = 2;
HANDED_AMBIDEXTROUS = 3;
}
Handed handed = 4;
reserved gender;
}
レキシカル スコーピング
Editions 構文は、機能ごとの許可されたターゲットのリストを使用して、レキシカル スコーピングをサポートしています。たとえば、最初のエディションでは、機能はファイル レベルまたは最低レベルの粒度でのみ指定できます。レキシカル スコーピングの実装により、ファイル全体にわたる機能のデフォルトの動作を設定し、メッセージ、フィールド、enum、enum 値、oneof、service、またはメソッド レベルでその動作をオーバーライドできます。上位レベル (ファイル、メッセージ) で行われた設定は、同じスコープ内 (フィールド、enum 値) で設定が行われていない場合に適用されます。明示的に設定されていない機能は、.proto ファイルに使用されているエディション バージョンで定義された動作に準拠します。
次のコード サンプルは、ファイル、フィールド、および enum レベルで設定されているいくつかの機能を示しています。
edition = "2023";
option features.enum_type = CLOSED;
message Person {
string name = 1;
int32 id = 2 [features.field_presence = IMPLICIT];
enum Pay_Type {
PAY_TYPE_UNSPECIFIED = 1;
PAY_TYPE_SALARY = 2;
PAY_TYPE_HOURLY = 3;
}
enum Employment {
option features.enum_type = OPEN;
EMPLOYMENT_UNSPECIFIED = 0;
EMPLOYMENT_FULLTIME = 1;
EMPLOYMENT_PARTTIME = 2;
}
Employment employment = 4;
}
上記の例では、presence 機能は IMPLICIT
に設定されています。設定されていない場合は、EXPLICIT
がデフォルトになります。Pay_Type
enum
は、ファイルレベルの設定が適用されるため、CLOSED
になります。ただし、Employment
enum
は、enum 内で設定されているため、OPEN
になります。
Prototiller
Prototiller ツールが起動されると、エディションへの移行およびエディション間の移行を容易にするための移行ガイドと移行ツールが提供されます。このツールを使用すると、次のことが可能になります。
- proto2 および proto3 定義ファイルを新しいエディション構文に大規模に変換する
- ファイルをあるエディションから別のエディションに移行する
- proto ファイルを他の方法で操作する
下位互換性
Protobuf Editions は、可能な限り破壊的でないように構築しています。たとえば、proto2 および proto3 定義をエディションベースの定義ファイルにインポートしたり、その逆も可能です。
// file myproject/foo.proto
syntax = "proto2";
enum Employment {
EMPLOYMENT_UNSPECIFIED = 0;
EMPLOYMENT_FULLTIME = 1;
EMPLOYMENT_PARTTIME = 2;
}
// file myproject/edition.proto
edition = "2023";
import "myproject/foo.proto";
proto2 または proto3 からエディションに移行すると、生成されたコードは変更されますが、ワイヤ形式は変更されません。エディション構文の proto 定義を使用して、proto2 および proto3 のデータ ファイルまたはファイル ストリームに引き続きアクセスできます。
文法変更
proto2 および proto3 と比較して、エディションにはいくつかの文法変更があります。
構文の説明。 syntax
要素の代わりに、edition
要素を使用します。
syntax = "proto2";
syntax = "proto3";
edition = "2028";
予約名。 フィールド名と enum 値名を予約するときに引用符で囲む必要がなくなりました。
reserved foo, bar;
グループ構文。 proto2 で利用可能なグループ構文は、エディションで削除されました。グループで使用されていた特別なワイヤ形式は、DELIMITED
メッセージ エンコーディングを使用することで引き続き利用できます。
必須ラベル。 proto2 でのみ利用可能な required
ラベルは、エディションでは利用できません。基盤となる機能は、features.field_presence=LEGACY_REQUIRED
を使用することで引き続き利用できます。