Skip to content

Field presence decides whether a reader can tell a field holding its zero value, an int at 0 or a string at "", apart from a field nobody set. Edition 2024 gives every singular field explicit presence by default, so a value of 0 and an absent field are different states. With IMPLICIT, that distinction disappears. A zero stops getting written at all, and a reader has no way to tell it from a field that was never set.

The two schemas that follow differ only in their field_presence setting, EXPLICIT or IMPLICIT. Every field in both messages is set to its zero value, the only case where the two behave differently.

Explicit presence
edition = "2024";

package demo.v1;

option features.field_presence = EXPLICIT;

message Settings {
  string label = 1;
  int32 retries = 2;
  bool verbose = 3;
}
JSON
{
  "label": "",
  "retries": 0,
  "verbose": false
}

With explicit presence, a field set to zero is still set, so the JSON includes it.

Implicit presence
edition = "2024";

package demo.v1;

option features.field_presence = IMPLICIT;

message Settings {
  string label = 1;
  int32 retries = 2;
  bool verbose = 3;
}
JSON
{}

With implicit presence, zero and unset are indistinguishable, so the JSON is empty.

The same zero value either costs two bytes or vanishes entirely, depending on the field's presence.

The explicit field writes its zero so a reader knows it was set. The implicit field's zero disappears.

schema.proto
edition = "2024";

package demo.v1;

message Counter {
  // Edition 2024 default: explicit presence.
  int32 explicit_count = 1;
  int32 implicit_count = 2 [features.field_presence = IMPLICIT];
}
Input JSON (editable)
{
  "explicitCount": 0,
  "implicitCount": 0
}
ProtoJSON

Compiling the schema...

Further Reading

Next

Required Fields

Why Protobuf dropped the required keyword, what went wrong with it, and how to enforce required semantics today with validation instead of the wire format.