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 <[email protected]> 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 [email protected]. > 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 [email protected]. To view this discussion visit https://groups.google.com/d/msgid/protobuf/CAKRmVH8E56q3HJhuoHxkOgYJrh3PM-0jW93cOAF9v4j%3Db4y3jg%40mail.gmail.com.
