Protobufのoptionalを理解したいメモ

公式サイト
https://protobuf.dev/

ふんわりとなんかセットしていなければデフォルトの0とかStringの空になったりするという知識しかなくて、そんなに詳しくないので、詳しくなりたい記事です。誤りがあれば教えて下さい。

## そもそもoptionalとは

Protobuf v3.15から使えるlabelのようです。

```proto
message Test1 {
  optional int32 a = 1;
}
```


v3.0でoptionalは消えたけど、
v3.15でoptionalが復活していて、これが推奨されているっぽいです。
https://matsudamper.hatenablog.com/entry/2022/08/22/202726
https://qiita.com/disc99/items/a8ac2a264f322bc6d6e5


## Optionalについてのproto3の公式説明

https://protobuf.dev/programming-guides/proto3/#field-labels

2つのどちらかの状態になる。
* 値がセットされていれば、明示的にセットされているかwireからパースされている。(wireとはなにかという説明がちょっとあんまり見つからなかったけど多分データのバイト列だと思う。)
* もしセットされていなければ、デフォルト値を返す。wireにはシリアライズされない。

>Specifying Field Labels
Message fields can be one of the following:
optional: An optional field is in one of two possible states:
> * the field is set, and contains a value that was explicitly set or parsed from the wire. It will be serialized to the wire.
> * the field is unset, and will return the default value. It will not be serialized to the wire.
You can check to see if the value was explicitly set.

## optionalとかつけていないデフォルトのときは？

`implicit field presence`になる。よく作られたMessageはこのフィールドを2個以上持たない。値がwireからパースされたかどうかは特定できない。デフォルト値でなければwireにシリアライズされる。詳しくはField Presenceへ。

> If no explicit field label is applied, the default field label, called “implicit field presence,” is assumed. (You cannot explicitly set a field to this state.) A well-formed message can have zero or one of this field (but not more than one). You also cannot determine whether a field of this type was parsed from the wire. An implicit presence field will be serialized to the wire unless it is the default value. For more on this subject, see Field Presence.

## No PresenceとExplicit Presenceとは？


no presence: 生成されたメッセージAPIがフィールドの値のみをストアする
explicit presence: 生成されたメッセージAPIがフィールドの値とその存在をストアする

つまり、explicit presenceだとクライアントがセットされているかどうかを知れる。

https://protobuf.dev/programming-guides/field_presence/

> Field presence is the notion of whether a protobuf field has a value. There are two different manifestations of presence for protobufs: no presence, where the generated message API stores field values (only), and explicit presence, where the API also stores whether or not a field has been set.

## Explicit Presenceになるのはどんなとき？

optionalがついてるときと、messageのときあと、Oneofs(分かっていない)のとき

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27388/d70e5652-8766-fef0-8436-66b11b2a318e.png)
https://protobuf.dev/programming-guides/field_presence/ より

## じゃあ実際のバイト列はどうなってるのさ？

optional fieldの存在しないことはバイト列の中に入れないこと(recordに入れない)で表現する。(つまり0とかでもバイト列にいれるってことかな)

https://protobuf.dev/programming-guides/encoding/#optional

> Optional and Repeated Elements
Missing optional fields are easy to encode: we just leave out the record if it’s not present. This means that “huge” protos with only a few fields set are quite sparse.
repeated fields are a bit more complicated. Ordinary (not packed) repeated fields emit one record for every element of the field. Thus, if we have

## 互換性

optionalじゃないprotoから作られたクライアントでは、optionalの情報が抜けるので、デフォルト値と同じ場合、optionalのhas_fooみたいなのが、falseになってしまう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27388/2df128fc-de2c-c2c9-ab33-9414d60ac502.png)

```
// Client A:
m_a.ParseFromString(Receive());  // from client B
assert(m_a.foo() == 0);          // OK
assert(m_a.has_foo());           // FAIL
```

https://protobuf.dev/programming-guides/field_presence/#considerations-for-change-compatibility より

## Proto Best Practicesの関連しそうなもの
https://protobuf.dev/programming-guides/dos-donts/

### Required Fieldを追加しない。

required fieldを追加しないでください。`// required` ドキュメントを代わりに使ってください。害があることがわかったので、proto3からは消されている。すべてのフィールドをoptionalかrepeatedにする。論理的にそのフィールドが必要なくなったときに、将来誰かが空の値や0を入れるかどうかわからないので。

>Don’t Add a Required Field
Never add a required field, instead add // required to document the API contract. Required fields are considered harmful by so many they were removed from proto3 completely. Make all fields optional or repeated. You never know how long a message type is going to last and whether someone will be forced to fill in your required field with an empty string or zero in four years when it’s no longer logically required but the proto still says it is.

For proto3 there are no required fields, so this advice does not apply.

### たくさんのフィールドを持たないようにする。

optionalだと存在するかのフィールドにもメモリを使っていて、クライアント側でメモリを使いすぎることになる。多すぎるとクラアントの言語仕様の壁にも当たることがある。

> Don’t Make a Message with Lots of Fields
Don’t make a message with “lots” (think: hundreds) of fields. In C++ every field adds roughly 65 bits to the in-memory object size whether it’s populated or not (8 bytes for the pointer and, if the field is declared as optional, another bit in a bitfield that keeps track of whether the field is set). When your proto grows too large, the generated code may not even compile (for example, in Java there is a hard limit on the size of a method ).

# まとめ
* optionalをつけるとExplicit Presenceになる。
* Explicit Presenceだと明示的にサーバー側でセットされた値かデフォルトかが取れる。
* optionalのバイト列での表現は、存在しないときはレコードがなく、あれば入っている。
* optionalでないクライアントから、optionalであるクライアントにデフォルト値のメッセージが送信された場合は、デフォルト値であればレコードがないので、optionalの対応が入っているクライアントからはサーバーが値をセットしていないように見える。
* 基本的には全部optionalになっているべき。
* わからなかったこと: optionalをつけないプロパティの使い所がわからなかった。互換性維持かな？

サーバーからクライアントが受け取るときの知れること。

| | バイト列上の値がない(レコードがない) | バイト列上の値がある(レコードがある) |
|--|--|---|
| optional | サーバーにより値がセットされていない | サーバーにより値がセットされている | 
| optionalなし | サーバーにより、デフォルト値がセットされた、またはセットされていない | サーバーにより値がセットされている |