Can you clarify a bit more what it is you are trying to achieve, and if there are any official implementations where Option/nullability is a pain point?
So in general the official Google Protobuf implementations do not expose Option<SomeMessage> or nullable-SomeMessage* in almost any of our implementations (as per the page I mentioned on the github issue: https://protobuf.dev/design-decisions/nullable-getters-setters/) If I'm following collectly, on all official implementations you should be able to get the behavior you're describing by just declaring the child messages and just don't check their has-bits. As far as the observable behavior is concerned all of the submessages being eagerly allocated or lazily allocated should be an implementation detail. proto2 `required` is considered strongly regretted and we discourage its use, but just considering the semantics of required with Prost shape of API, I think it could not omit the Option<> on required message fields because the semantic of required is inherently only on wire and not in-memory: its the normal design that you would first construct a message without the required fields set, set them, and then eventually serialize it. Even in terms of data that was from parsed data, most Google official Protobuf implementations support a "parsePartial" which means "parse these bytes but don't enforce required, and then me write the logic against hassers directly". On Mon, Jun 2, 2025 at 3:46 PM Yordis Prieto <yordis.pri...@gmail.com> wrote: > As requested by [@esrauchg](https://github.com/esrauchg), moving the > conversation here: > https://github.com/tokio-rs/prost/issues/1031#issuecomment-2932003751. > > Although this originated in a `prost` discussion, the underlying issue > concerns > how the official protobuf toolchain treats field presence. The intent here > is to > clarify the expected stance and behavior of the generator. > > **What language does this apply to?** > > Primarily **Rust**, but also interested in **Go** and **TypeScript** as > they > relate to protobuf code generation. > > **Describe the problem you are trying to solve.** > > The generated types are structurally imprecise: **all fields are wrapped > in `Option<T>`**, > even those marked as `required` (explicitly or implicitly) in the proto > definition. > > This leads to: > > - Verbose boilerplate (`Some(...)` wrappers) > - Increased cognitive overhead > - Risk of runtime errors when unwrapping values that should be guaranteed > > ```rust > fn calculate_minimum_bid_increment(cmd: &StartAuctionRun) -> Result< > MoneyAmount, Error> { > match &cmd.minimum_bid_increment_policy { > Some(policy) => match &policy.policy { > Some(minimum_bid_increment_policy::Policy::Fixed(fixed_policy)) => { > //... > } > // ... > // NOTE: this should never happen, it is required > None => Err(Error::MinimumBidIncrementPolicyRequired), > }, > // NOTE: this should never happen, it is required > None => Err(Error::MinimumBidIncrementPolicyRequired), > } > } > ``` > > Despite the domain clearly requiring this field, the type system does not > enforce it. Structurally speaking. > > We're using protobuf as the canonical schema for all: > > - Commands > - Events > - Aggregate Snapshot > > That applies across multiple language runtimes (via WASM modules) and is > critical for: > > - Schema evolution and change detection > - Consistent interop across services > - Serialization correctness > - Avoiding runtime reflection or manual encoders/decoders > > We treat protobuf-generated types as the source of truth and only validate > `commands` post-deserialization (or via protovalidate extension). > > Here is the existing callbacks (thus far): > > ```rust > pub type InitialState<State> = fn() -> State; > pub type IsTerminal<State> = fn(state: State) -> bool; > pub type Decide<State, Command, Event, Error> = > fn(state: &State, command: Command) -> Result<Decision<State, Command, > Event, Error>, Error>; > pub type Evolve<State, Event> = fn(state: &State, event: Event) -> State; > > pub type GetStreamId<Command> = fn(Command) -> String; > pub type IsOrigin<Event> = fn(Event) -> bool; > pub type GetEventID<Event> = fn(Event) ->String; > ``` > > **Describe the solution you'd like** > > The code generator should respect the `required` field modifier in > `.proto` > definitions, emitting non-optional Rust fields where appropriate. > > That would: > > - Better align with schema intent > - Eliminate unnecessary `Option<T>` wrappers > - Improve safety and ergonomics > > **Describe alternatives you've considered** > > Creating application-level copies of protobuf types, but such types > have very little value in our context. The distintion between application > and serialization matters better little to us, especially when protobuffer > files can not break change. > > **Additional context** > > - https://github.com/tokio-rs/prost/pull/1286 > - https://github.com/tokio-rs/prost/issues/1031 > > -- > You received this message because you are subscribed to the Google Groups > "Protocol Buffers" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to protobuf+unsubscr...@googlegroups.com. > To view this discussion visit > https://groups.google.com/d/msgid/protobuf/b5386744-e770-430e-bccb-959409a05641n%40googlegroups.com > <https://groups.google.com/d/msgid/protobuf/b5386744-e770-430e-bccb-959409a05641n%40googlegroups.com?utm_medium=email&utm_source=footer> > . > -- You received this message because you are subscribed to the Google Groups "Protocol Buffers" group. To unsubscribe from this group and stop receiving emails from it, send an email to protobuf+unsubscr...@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/protobuf/CAKRmVH8E56q3HJhuoHxkOgYJrh3PM-0jW93cOAF9v4j%3Db4y3jg%40mail.gmail.com.