Protobuf Editions の概要

Protobuf Editions 機能の概要。

Protobuf Editions は、Protocol Buffers で使用してきた proto2 および proto3 の指定に代わるものです。proto 定義ファイルの先頭に syntax = "proto2" または syntax = "proto3" を追加する代わりに、edition = "2023" などのエディション番号を使用して、ファイルが持つデフォルトの動作を指定します。エディションを使用すると、言語を段階的に進化させることができます。

古いバージョンが持っていたハードコードされた動作の代わりに、エディションは機能ごとのデフォルト値 (動作) を持つ機能のコレクションを表します。機能は、protoc、コード ジェネレーター、および protobuf ランタイムの動作を指定する、ファイル、メッセージ、フィールド、enum などに関するオプションです。選択したエディションのデフォルトの動作がニーズに合わない場合は、これらの異なるレベル (ファイル、メッセージ、フィールド、…) で動作を明示的にオーバーライドできます。オーバーライドをさらにオーバーライドすることもできます。このトピックの後半のレキシカル スコーピングに関するセクションで、その詳細について説明します。

最新リリースされたエディションは 2023 です。

機能のライフサイクル

エディションは、機能のライフサイクルの基本的な増分を提供します。機能には、導入、デフォルトの動作の変更、非推奨、そして削除という予想されるライフサイクルがあります。例:

  1. エディション 2031 は、デフォルト値が falsefeature.amazing_new_feature を作成します。この値は、以前のすべてのエディションと同じ動作を維持します。つまり、影響がないことがデフォルトです。

  2. 開発者は、.proto ファイルを edition = "2031" に更新します。

  3. 後のエディション (エディション 2033 など) では、feature.amazing_new_feature のデフォルトを false から true に切り替えます。これはすべての proto で望ましい動作であり、protobuf チームが機能を作成した理由です。

    Prototiller ツールを使用して以前のバージョンの proto ファイルをエディション 2033 に移行すると、以前の動作を維持するために必要な場合に明示的な feature.amazing_new_feature = false エントリが追加されます。開発者は、新しい動作を .proto ファイルに適用したい場合に、これらの新しく追加された設定を削除します。

  1. ある時点で、feature.amazing_new_feature はエディションで非推奨とマークされ、後のエディションで削除されます。

    機能が削除されると、その動作のコード ジェネレーターと、それをサポートするランタイム ライブラリも削除される可能性があります。ただし、タイムラインは寛大になります。ライフサイクルの前の手順の例に従うと、非推奨はエディション 2034 で発生する可能性がありますが、約 2 年後のエディション 2036 まで削除されない可能性があります。機能を削除すると、常にメジャー バージョンのバンプが開始されます。

このライフサイクルにより、非推奨の機能を使用していない .proto ファイルは、あるエディションから次のエディションへのnoop アップグレードになります。Google の移行期間と非推奨期間の全期間を利用して、コードをアップグレードできます。

上記のライフサイクルの例では、機能にブール値を使用しましたが、機能では enum も使用できます。たとえば、features.field_presence には、LEGACY_REQUIREDEXPLICIT、および 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 を使用することで引き続き利用できます。