HomeBlogProjects

思考 ProtoBuf 中的可选与必选字段

sorcererxw

没有 optional 的世界

在很长的一段时间里,ProtoBuf3 不支持 ProtoBuf2 当中 optional 关键字。正如我在gRPC 与『面向扩展编程』 中提到的,我认为所有字段均为可选,有益于未来的扩展性,避免加入一个新的字段,导致调用方的代码类型出错。

比如在 TypeScript 当中,如果一个字段未修饰为 optional,那么初始化这个 object 就必须要完整的定义所有字段。如果有无数个调用方在使用这个 Request 类型,显然就很难在其中增加字段了。

一般来说,这个时候我们希望让一个字段 nullable 有两种方法:

上面的方法虽然不是很优雅,但可以 work!!!

重新引入 optional

不过最近 ProtoBuf v3.15.0 正式重新引入了 optional 关键字,着实令我有点 mind blowing。

Release Protocol Buffers v3.15.0 · protocolbuffers/protobuf
Protocol Compiler Optional fields for proto3 are enabled by default, and no longer require the --experimental_allow_proto3_optional flag. C++ MessageDifferencer: fixed bug when using custom igno...
https://github.com/protocolbuffers/protobuf/releases/tag/v3.15.0
syntax = "proto3";

message Request {
	string arg1 = 1;
	optional string arg2 = 2;
}
那么不带 optional 的字段到底是可选还是必选?

带着这个疑问,基于 TypeScript 和 Go 的 gRPC 做了一些实验,optional 和 non-optional 字段的不同表现,总结出如下的表现:

这样就很清晰了,在没有 optional 的时候,所有字段并不是真正意义上的可选字段,只是对于调用方来说,可以选择性地初始化部分字段,但是对于被调用方来说,需要依赖框架或者语言本身的特性去补齐为定义字段,这样在使用的时候才不会产生空指针错误。

如果一个字段是可选字段,明确有一个空的状态,这个状态需要调用和被调用双方都能感知,就需要将其标记为 optional。