Skip to content

Conversation

@Jozott00
Copy link
Collaborator

@Jozott00Jozott00 commented Dec 1, 2025

Subsystem
Protobuf

Problem Description
Even if a field in a message is optional, we can't make it nullable. This is because fields may either have a default value, or we want to be able to access a default value in a nested field such that the parent object must not be null.

However, users must be able to distinguish between a field that was manually set with the default value and one that was not actively set.

Solution
This PR solves this problem by adding a presence API to each message that contains fields with presence tracking.

val myMessage =Message{myField ="value" } myMessage.presence.hasField

presence is an extension getter on the generated message interface that provides a hasXYZ getter for all fields with tracked/explicit presence.
This intermediate presence object is employed to ensure concise code completion and minimize the number of suggestions provided by the IDE.

The only field type that lacks a hasXYZ getter while its presence is tracked is the oneof field. Since oneof fields are nullable, they are null if they are not set.

@Jozott00Jozott00 self-assigned this Dec 1, 2025
@Jozott00Jozott00 added the feature New feature or request label Dec 1, 2025
@Jozott00Jozott00 requested a review from Mr3zeeDecember 1, 2025 13:07
@Mr3zee
Copy link
Member

Mr3zee commented Dec 1, 2025

@Jozott00 great and quick job!
I won't have time to look into it closely today, but I noticed two things after a quick glance:

  • Presence class that is generated is in __rpc_internal package, which is fine for it's implementation, but I think we should expose to the user an interface with hasField properties in .ext.kt and keep the impl for it in __rpc_internal, so that the navigation doesn't bring you to internal when possible
  • No generated docs for .presence

@Jozott00
Copy link
CollaboratorAuthor

Jozott00 commented Dec 1, 2025

@Mr3zee

  1. Yes, that is probably better. Any suggestions on how to name the interface? Currently it is an inner class which makes it easy. When moving it to the .ext.kt should I name the interface <OuterClass><InnerClass>Presence? (assuming it's the interface for a nested message)
  2. Good point, I will add documentation

@Mr3zee
Copy link
Member

@Jozott00 good question, I don't know 😄 Extension classes would be great here

@Mr3zee
Copy link
Member

what about

interfaceOuterClassPresence{val hasField:BooleaninterfaceInnerClass{val hasInnerMessageField:Boolean } }

@Mr3zee
Copy link
Member

Also, please update the conformance regeneration

Copy link
Member

@Mr3zeeMr3zee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noice, great job!

@Jozott00Jozott00 merged commit 15d00aa into grpc-commonDec 15, 2025
6 of 7 checks passed
@Jozott00Jozott00 deleted the grpc/presence-check branch December 15, 2025 09:56
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

featureNew feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@Jozott00@Mr3zee