エディションの機能設定

Protobuf Editions の機能とそれが Protobuf の動作にどう影響するか。

このトピックでは、Edition 2023 に含まれる機能の概要を説明します。以降のエディションの機能もこのトピックに追加されます。新しいエディションはニュースセクションで発表されます。

新しいスキーマ定義コンテンツで機能設定を構成する前に、それらを使用する理由を理解していることを確認してください。機能のカーゴ・カルトは避けてください。

Prototiller

Prototiller は、proto2 および proto3 定義ファイルを Editions 構文に変換するコマンドラインツールです。まだリリースされていませんが、このトピック全体で参照されています。

機能

以下のセクションには、Edition 2023 の機能を使用して構成できるすべての動作が含まれています。proto2 または proto3 の動作の保持では、proto 定義ファイルが proto2 または proto3 ファイルのように動作するように、デフォルトの動作をオーバーライドする方法を示します。エディションと機能が連携して動作を設定する方法の詳細については、Protobuf Editions 概要を参照してください。

機能設定は異なるレベルで適用されます

ファイルレベル: これらの設定は、オーバーライド設定を持たないすべての要素(メッセージ、フィールド、列挙型など)に適用されます。

非ネスト: メッセージ、列挙型、サービスは、ファイルレベルで行われた設定をオーバーライドできます。これらは、オーバーライドされない限り、それらの中のすべて(メッセージフィールド、列挙値)に適用されますが、他の並列のメッセージや列挙型には適用されません。

ネスト: Oneof、メッセージ、列挙型は、ネストされているメッセージからの設定をオーバーライドできます。

最下位レベル: フィールド、拡張機能、列挙値、拡張範囲、およびメソッドは、設定をオーバーライドできる最下位レベルです。

以下の各セクションには、機能が適用できるスコープを示すコメントがあります。以下のサンプルは、各スコープに適用されたモック機能を示しています。

edition = "2023";

// File-level scope definition
option features.bar = BAZ;

enum Foo {
  // Enum (non-nested scope) definition
  option features.bar = QUX;

  A = 1;
  B = 2;
}

message Corge {
  // Message (non-nested scope) definition
  option features.bar = QUUX;

  message Garply {
    // Message (nested scope) definition
    option features.bar = WALDO;
    string id = 1;
  }

  // Field (lowest-level scope) definition
  Foo A = 1 [features.bar = GRAULT];
}

この例では、最下位レベルのスコープ機能定義にある「GRAULT」設定が、非ネストスコープの「QUUX」設定をオーバーライドします。そして、Garply メッセージ内では、「WALDO」が「QUUX」をオーバーライドします。

features.enum_type

この機能は、定義されたセットに含まれていない enum 値の処理方法の動作を設定します。オープンおよびクローズド enum の詳細については、Enum の動作を参照してください。

この機能は proto3 ファイルには影響しないため、このセクションには proto3 ファイルの変更前後の例はありません。

利用可能な値

  • CLOSED: クローズド enum は、範囲外の enum 値を未知のフィールドセットに格納します。
  • OPEN: オープン enum は、範囲外の値を直接そのフィールドにパースします。

適用可能なスコープ: ファイル、Enum

Edition 2023 のデフォルトの動作: OPEN

proto2 の動作: CLOSED

proto3 の動作: OPEN

注: 異なるスキーマ要素の機能設定は異なるスコープを持ちます。

以下のコードサンプルは proto2 ファイルを示しています

syntax = "proto2";

enum Foo {
  A = 2;
  B = 4;
  C = 6;
}

Prototillerを実行した後、同等のコードは次のようになります。

edition = "2023";

enum Foo {
  // Setting the enum_type feature overrides the default OPEN enum
  option features.enum_type = CLOSED;
  A = 2;
  B = 4;
  C = 6;
}

features.field_presence

この機能は、フィールドの存在、つまり Protobuf フィールドが値を持っているかどうかの追跡動作を設定します。

利用可能な値

  • LEGACY_REQUIRED: このフィールドはパースとシリアル化に必須です。明示的に設定された値は、デフォルト値と同じであっても、ワイヤ上にシリアル化されます。
  • EXPLICIT: このフィールドは明示的な存在追跡を行います。明示的に設定された値は、デフォルト値と同じであっても、ワイヤ上にシリアル化されます。単一のプリミティブフィールドの場合、EXPLICITに設定されたフィールドに対してhas_*関数が生成されます。
  • IMPLICIT: このフィールドは存在追跡を行いません。デフォルト値は、明示的に設定されていてもワイヤ上にシリアル化されません。IMPLICITに設定されたフィールドに対してhas_*関数は生成されません。

適用可能なスコープ: ファイル、フィールド

Edition 2023 のデフォルトの動作: EXPLICIT

proto2 の動作: EXPLICIT

proto3 の動作: optionalラベルを持つフィールドでない限りIMPLICITです。その場合はEXPLICITのように動作します。詳細については、Proto3 API における存在を参照してください。

注: 異なるスキーマ要素の機能設定は異なるスコープを持ちます。

以下のコードサンプルは proto2 ファイルを示しています

syntax = "proto2";

message Foo {
  required int32 x = 1;
  optional int32 y = 2;
  repeated int32 z = 3;
}

Prototiller を実行した後、同等のコードは次のようになります。

edition = "2023";

message Foo {
  // Setting the field_presence feature retains the proto2 required behavior
  int32 x = 1 [features.field_presence = LEGACY_REQUIRED];
  int32 y = 2;
  repeated int32 z = 3;
}

以下に proto3 ファイルを示します。

syntax = "proto3";

message Bar {
  int32 x = 1;
  optional int32 y = 2;
  repeated int32 z = 3;
}

Prototiller を実行した後、同等のコードは次のようになります。

edition = "2023";
// Setting the file-level field_presence feature matches the proto3 implicit default
option features.field_presence = IMPLICIT;

message Bar {
  int32 x = 1;
  // Setting the field_presence here retains the explicit state that the proto3
  // field has because of the optional syntax
  int32 y = 2 [features.field_presence = EXPLICIT];
  repeated int32 z = 3;
}

required および optional ラベルは Editions には存在しなくなりました。対応する動作はfield_presence機能によって明示的に設定されるためです。

features.json_format

この機能は、JSON のパースとシリアル化の動作を設定します。

この機能は proto3 ファイルには影響しないため、このセクションには proto3 ファイルの変更前後の例はありません。Editions の動作は proto3 の動作と一致します。

利用可能な値

  • ALLOW: ランタイムは JSON のパースとシリアル化を許可する必要があります。JSON への明確なマッピングが存在することを確認するために、proto レベルでチェックが適用されます。
  • LEGACY_BEST_EFFORT: ランタイムは JSON のパースとシリアル化に最善を尽くします。ランタイムで未指定の動作(多対1または1対多のマッピングなど)を招く可能性がある特定の proto が許可されます。

適用可能なスコープ: ファイル、メッセージ、Enum

Edition 2023 のデフォルトの動作: ALLOW

proto2 の動作: LEGACY_BEST_EFFORT

proto3 の動作: ALLOW

注: 異なるスキーマ要素の機能設定は異なるスコープを持ちます。

以下のコードサンプルは proto2 ファイルを示しています

syntax = "proto2";

message Foo {
  // Warning only
  string bar = 1;
  string bar_ = 2;
}

Prototiller を実行した後、同等のコードは次のようになります。

edition = "2023";
option features.json_format = LEGACY_BEST_EFFORT;

message Foo {
  string bar = 1;
  string bar_ = 2;
}

features.message_encoding

この機能は、シリアル化時にフィールドをエンコードする際の動作を設定します。

この機能は proto3 ファイルには影響しないため、このセクションには proto3 ファイルの変更前後の例はありません。

言語によっては、「グループのような」フィールドは、proto2 との後方互換性を提供するために、生成されたコードやテキスト形式で予期しない大文字化が行われる場合があります。メッセージフィールドが「グループのような」であるのは、以下のすべての条件が満たされた場合です。

  • DELIMITED メッセージエンコーディングが指定されている
  • メッセージ型がフィールドと同じスコープで定義されている
  • フィールド名が型名の小文字と完全に一致する

利用可能な値

  • LENGTH_PREFIXED: フィールドは、メッセージ構造で説明されている LEN ワイヤタイプを使用してエンコードされます。
  • DELIMITED: メッセージ型フィールドは、グループとしてエンコードされます。

適用可能なスコープ: ファイル、フィールド

Edition 2023 のデフォルトの動作: LENGTH_PREFIXED

proto2 の動作: グループはデフォルトでDELIMITEDですが、それ以外はLENGTH_PREFIXEDです。

proto3 の動作: LENGTH_PREFIXED。Proto3 はDELIMITEDをサポートしていません。

注: 異なるスキーマ要素の機能設定は異なるスコープを持ちます。

以下のコードサンプルは proto2 ファイルを示しています

syntax = "proto2";

message Foo {
  group Bar = 1 {
    optional int32 x = 1;
    repeated int32 y = 2;
  }
}

Prototiller を実行した後、同等のコードは次のようになります。

edition = "2023";

message Foo {
  message Bar {
    int32 x = 1;
    repeated int32 y = 2;
  }
  Bar bar = 1 [features.message_encoding = DELIMITED];
}

features.repeated_field_encoding

この機能は、Editions でrepeatedフィールドの proto2/proto3 のpackedオプションが移行されたものです。

利用可能な値

  • PACKED: プリミティブ型のRepeatedフィールドは、各要素が連結された単一の LEN レコードとしてエンコードされます。
  • EXPANDED: Repeatedフィールドは、各値に対してフィールド番号とともにそれぞれエンコードされます。

適用可能なスコープ: ファイル、フィールド

Edition 2023 のデフォルトの動作: PACKED

proto2 の動作: EXPANDED

proto3 の動作: PACKED

注: 異なるスキーマ要素の機能設定は異なるスコープを持ちます。

以下のコードサンプルは proto2 ファイルを示しています

syntax = "proto2";

message Foo {
  repeated int32 bar = 6 [packed=true];
  repeated int32 baz = 7;
}

Prototiller を実行した後、同等のコードは次のようになります。

edition = "2023";
option features.repeated_field_encoding = EXPANDED;

message Foo {
  repeated int32 bar = 6 [features.repeated_field_encoding=PACKED];
  repeated int32 baz = 7;
}

以下に proto3 ファイルを示します。

syntax = "proto3";

message Foo {
  repeated int32 bar = 6;
  repeated int32 baz = 7 [packed=false];
}

Prototiller を実行した後、同等のコードは次のようになります。

edition = "2023";

message Foo {
  repeated int32 bar = 6;
  repeated int32 baz = 7 [features.repeated_field_encoding=EXPANDED];
}

features.utf8_validation

この機能は、文字列がどのように検証されるかを設定します。これは、それをオーバーライドする言語固有のutf8_validation機能がある場合を除き、すべての言語に適用されます。Java 言語固有の機能については、features.(pb.java).utf8_validationを参照してください。

この機能は proto3 ファイルには影響しないため、このセクションには proto3 ファイルの変更前後の例はありません。

利用可能な値

  • VERIFY: ランタイムは UTF-8 を検証する必要があります。これは proto3 のデフォルトの動作です。
  • NONE: このフィールドは、ワイヤ上で未検証のbytesフィールドのように動作します。パーサーは、無効な文字を置換するなど、予測できない方法でこの種のフィールドを処理する可能性があります。これは proto2 のデフォルトの動作です。

適用可能なスコープ: ファイル、フィールド

Edition 2023 のデフォルトの動作: VERIFY

proto2 の動作: NONE

proto3 の動作: VERIFY

注: 異なるスキーマ要素の機能設定は異なるスコープを持ちます。

以下のコードサンプルは proto2 ファイルを示しています

syntax = "proto2";

message MyMessage {
  string foo = 1;
}

Prototiller を実行した後、同等のコードは次のようになります。

edition = "2023";

message MyMessage {
  string foo = 1 [features.utf8_validation = NONE];
}

言語固有の機能

一部の機能は特定の言語に適用され、他の言語の同じ proto には適用されません。これらの機能を使用するには、その言語のランタイムから対応する *_features.proto ファイルをインポートする必要があります。以下のセクションの例でこれらのインポートを示します。

features.(pb.cpp/pb.java).legacy_closed_enum

言語: C++、Java

この機能は、オープンな enum 型を持つフィールドがクローズド enum のように動作すべきかどうかを決定します。これにより、エディションは proto2 および proto3 からの Java および C++ における非準拠の動作を再現できます。

この機能は proto3 ファイルには影響しないため、このセクションには proto3 ファイルの変更前後の例はありません。

利用可能な値

  • true: enum_typeに関わらず、enum をクローズドとして扱います。
  • false: enum_typeに設定されているものを尊重します。

適用可能なスコープ: ファイル、フィールド

Edition 2023 のデフォルトの動作: false

proto2 の動作: true

proto3 の動作: false

注: 異なるスキーマ要素の機能設定は異なるスコープを持ちます。

以下のコードサンプルは proto2 ファイルを示しています

syntax = "proto2";

import "myproject/proto3file.proto";

message Msg {
  myproject.proto3file.Proto3Enum name = 1;
}

Prototiller を実行した後、同等のコードは次のようになります。

edition = "2023";

import "myproject/proto3file.proto";

import "google/protobuf/cpp_features.proto";
import "google/protobuf/java_features.proto";

message Msg {
  myproject.proto3file.Proto3Enum name = 1 [
    features.(pb.cpp).legacy_closed_enum = true,
    features.(pb.java).legacy_closed_enum = true
  ];
}

features.(pb.cpp).string_type

言語: C++

この機能は、生成されたコードが文字列フィールドをどのように扱うべきかを決定します。これは proto2 および proto3 のctypeオプションを置き換え、新しいstring_view機能を提供します。Edition 2023 では、フィールドでctypeまたはstring_typeのいずれかを指定できますが、両方を指定することはできません。

利用可能な値

  • VIEW: フィールドのstring_viewアクセサを生成します。これは将来のエディションでデフォルトになります。
  • CORD: フィールドのCordアクセサを生成します。
  • STRING: フィールドのstringアクセサを生成します。

適用可能なスコープ: ファイル、フィールド

Edition 2023 のデフォルトの動作: STRING

proto2 の動作: STRING

proto3 の動作: STRING

注: 異なるスキーマ要素の機能設定は異なるスコープを持ちます。

以下のコードサンプルは proto2 ファイルを示しています

syntax = "proto2";

message Foo {
  optional string bar = 6;
  optional string baz = 7 [ctype = CORD];
}

Prototiller を実行した後、同等のコードは次のようになります。

edition = "2023";

import "google/protobuf/cpp_features.proto";

message Foo {
  string bar = 6;
  string baz = 7 [features.(pb.cpp).string_type = CORD];
}

以下に proto3 ファイルを示します。

syntax = "proto3"

message Foo {
  string bar = 6;
  string baz = 7 [ctype = CORD];
}

Prototiller を実行した後、同等のコードは次のようになります。

edition = "2023";

import "google/protobuf/cpp_features.proto";

message Foo {
  string bar = 6;
  string baz = 7 [features.(pb.cpp).string_type = CORD];
}

features.(pb.java).utf8_validation

言語: Java

この言語固有の機能により、Java のフィールドレベルでのファイルレベル設定をオーバーライドできます。

この機能は proto3 ファイルには影響しないため、このセクションには proto3 ファイルの変更前後の例はありません。

利用可能な値

  • DEFAULT: 動作はfeatures.utf8_validationで設定されたものと一致します。
  • VERIFY: ファイルレベルのfeatures.utf8_validation設定をオーバーライドし、Java のみVERIFYを強制します。

適用可能なスコープ: フィールド、ファイル

Edition 2023 のデフォルトの動作: DEFAULT

proto2 の動作: DEFAULT

proto3 の動作: DEFAULT

注: 異なるスキーマ要素の機能設定は異なるスコープを持ちます。

以下のコードサンプルは proto2 ファイルを示しています

syntax = "proto2";

option java_string_check_utf8=true;

message MyMessage {
  string foo = 1;
  string bar = 2;
}

Prototiller を実行した後、同等のコードは次のようになります。

edition = "2023";

import "google/protobuf/java_features.proto";

option features.utf8_validation = NONE;
option features.(pb.java).utf8_validation = VERIFY;
message MyMessage {
  string foo = 1;
  string bar = 2;
}

features.(pb.java).large_enum

言語: Java

この言語固有の機能により、コンパイラエラーを引き起こすことなく、Java で大規模な enum を処理する新しい機能を採用できます。

これは新しい動作であるため、proto2 または proto3 のスキーマ定義ファイルには影響しません。

利用可能な値

  • true: Java の enum は新しい機能を使用します。
  • false: Java の enum は引き続き Java の enum を使用します。

適用可能なスコープ: Enum

Edition 2023 のデフォルトの動作: false

proto2 の動作: false

proto3 の動作: false

注: 異なるスキーマ要素の機能設定は異なるスコープを持ちます。

proto2 または proto3 の動作の保持

エディション形式に移行したいが、生成コードの動作方法の更新にはまだ対応したくない場合があります。このセクションでは、Edition 2023 の proto が proto2 または proto3 ファイルのように動作するように、Prototiller ツールが .proto ファイルに行う変更を示します。

これらの変更がファイルレベルで行われると、proto2 または proto3 のデフォルトが適用されます。より低いレベル(メッセージレベル、フィールドレベル)でオーバーライドして、追加の動作の違い(必須、proto3 の optionalなど)を考慮したり、定義をほとんど proto2 または proto3 のようにしたい場合に適用できます。

Prototiller を使用しない特定の理由がない限り、Prototiller の使用をお勧めします。Prototiller を使用せずにこれらすべてを手動で適用するには、以下のセクションのコンテンツを .proto ファイルの先頭に追加してください。

Proto2 の動作

edition = "2023";

import "google/protobuf/cpp_features.proto";
import "google/protobuf/java_features.proto";

option features.field_presence = EXPLICIT;
option features.enum_type = CLOSED;
option features.repeated_field_encoding = EXPANDED;
option features.json_format = LEGACY_BEST_EFFORT;
option features.utf8_validation = NONE;
option features.(pb.cpp).legacy_closed_enum = true;
option features.(pb.java).legacy_closed_enum = true;

Proto3 の動作

// proto3 behaviors
edition = "2023";

import "google/protobuf/cpp_features.proto";
import "google/protobuf/java_features.proto";

option features.field_presence = IMPLICIT;
option features.enum_type = OPEN;
// `packed=false` needs to be transformed to field-level repeated_field_encoding
// features in Editions syntax
option features.json_format = ALLOW;
option features.utf8_validation = VERIFY;
option features.(pb.cpp).legacy_closed_enum = false;
option features.(pb.java).legacy_closed_enum = false;

注意点と例外

このセクションでは、Prototiller を使用しない場合に手動で行う必要がある変更を示します。

前のセクションで示したファイルレベルのデフォルトを設定すると、ほとんどの場合でデフォルトの動作が設定されますが、いくつかの例外があります。

  • optional: optionalラベルのすべてのインスタンスを削除し、ファイルデフォルトがIMPLICITの場合は、features.field_presenceEXPLICITに変更します。
  • required: requiredラベルのすべてのインスタンスを削除し、フィールドレベルでfeatures.field_presence=LEGACY_REQUIREDオプションを追加します。
  • groups: groupsを個別のメッセージに展開し、フィールドレベルでfeatures.message_encoding = DELIMITEDオプションを追加します。詳細については、features.message_encodingを参照してください。
  • java_string_check_utf8: このファイルオプションを削除し、features.(pb.java).utf8_validationに置き換えます。言語固有の機能で説明されているように、Java の機能をインポートする必要があります。
  • packed: エディション形式に変換された proto2 ファイルの場合、Proto2 の動作で設定したEXPANDEDの動作が不要な場合は、packedフィールドオプションを削除し、フィールドレベルで[features.repeated_field_encoding=PACKED]を追加します。エディション形式に変換された proto3 ファイルの場合、デフォルトの proto3 の動作が不要な場合は、フィールドレベルで[features.repeated_field_encoding=EXPANDED]を追加します。