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.

Reply via email to