null 許容のセッター/ゲッターはサポートされない
Protobufが、nullフレンドリーな言語(特にKotlin、C#、Rust)でnull許容ゲッター/セッターをサポートしてほしいというフィードバックを一部の方々から受けています。これらの言語を使用している人々にとって役立つ機能であるように思えますが、設計上のトレードオフがあり、Protobufチームはそれらを実装しないことを選択しました。
明示的な存在は、null可能性の伝統的な概念に直接対応するものではありません。これは微妙ですが、明示的な存在の哲学は、「フィールドはnull許容ではありませんが、フィールドに明示的に値が割り当てられたかどうかを検出できます。割り当てられていない場合、通常のアクセスでは何らかのデフォルト値が表示されますが、必要に応じてフィールドがアクティブに書き込まれたかどうかを確認できます。」に近いものです。
null許容フィールドを持たない最大の理由は、.protoファイルで指定されたデフォルト値の意図された動作です。設計上、設定されていないフィールドに対してゲッターを呼び出すと、そのフィールドのデフォルト値が返されます。
注: C#は、メッセージフィールドをnull許容として扱います。この他の言語との不整合は、不変メッセージの欠如に起因しており、共有される不変のデフォルトインスタンスを作成することが不可能であるためです。メッセージフィールドはデフォルト値を持つことができないため、これに機能的な問題はありません。
例として、この.protoファイルを考えてみましょう。
message Msg { Child child = 1; }
message Child { Grandchild grandchild = 1; }
message Grandchild { int32 foo = 1 [default = 72]; }
と対応するKotlinゲッター
// With our API where getters are always non-nullable:
msg.child.grandchild.foo == 72
// With nullable submessages the ?. operator fails to get the default value:
msg?.child?.grandchild?.foo == null
// Or verbosely duplicating the default value at the usage site:
(msg?.child?.grandchild?.foo ?: 72)
と対応するRustゲッター
// With our API:
msg.child().grandchild().foo() // == 72
// Where every getter is an Option<T>, verbose and no default observed
msg.child().map(|c| c.grandchild()).map(|gc| gc.foo()) // == Option::None
// For the rare situations where code may want to observe both the presence and
// value of a field, the _opt() accessor which returns a custom Optional type
// can also be used here (the Optional type is similar to Option except can also
// be aware of the default value):
msg.child().grandchild().foo_opt() // Optional::Unset(72)
もしnull許容ゲッターが存在するなら、それは必然的にユーザーが指定したデフォルト値(代わりにnullを返す)を無視することになり、驚くべき、一貫性のない動作につながります。null許容ゲッターのユーザーがフィールドのデフォルト値にアクセスしたい場合、nullが返された場合にデフォルト値を使用するための独自のカスタム処理を記述する必要があり、nullゲッターによるよりクリーンで簡単なコードという想定される利点が失われます。
同様に、null許容セッターも提供していません。その動作は直感的ではないためです。設定してから取得すると、常に同じ値が返されるわけではなく、設定を呼び出しても、フィールドのhasビットに影響を与えるのは時々だけです。
メッセージ型フィールドは常に明示的な存在フィールド(ハザー付き)であることに注意してください。Proto3では、スカラーフィールドは明示的に`optional`とマークされていない限り、暗黙的な存在(ハザーなし)をデフォルトとしますが、Proto2は暗黙的な存在をサポートしていません。エディションでは、暗黙的な存在機能が使用されない限り、明示的な存在がデフォルトの動作です。ほぼすべてのフィールドが明示的な存在を持つという将来の期待により、null許容ゲッターに伴う人間工学上の懸念は、Proto3ユーザーにとってよりも大きな懸念になると予想されます。
これらの問題により、null許容セッター/ゲッターはデフォルト値の使用方法を根本的に変えてしまいます。その有用性は理解できるものの、導入される不整合と困難さは価値がないと判断しました。