Protocol Buffers (Protobuf) support enum types, which allow you to define a fixed set of named constants. Enums are useful when a field should only take one value from a predefined list, such as status codes, categories, or types.
A typical real-world example is a car’s body style, where the value should be one of a known set like sedan, SUV, or coupe. Instead of using a free-form string, an enum ensures type safety, compact serialization, and consistent values across systems.
Why Use Enums in Protobuf
Using enums provides several benefits:
- They restrict values to a known set, which prevents invalid data from being serialized.
- They improve readability because names like
SEDANorSUVare clearer than numeric codes. - They serialize efficiently because Protobuf encodes enum values as integers under the hood.
- They make APIs more self-documenting since the allowed values are visible in the schema.
Defining an Enum
Enums can be defined directly inside a .proto file. You do not need a separate proto file unless your design requires reuse across multiple schemas.
Example:
enum BodyStyle {
BODY_STYLE_UNSPECIFIED = 0;
SEDAN = 1;
COUPE = 2;
SUV = 3;
}
The “First Value Must Be Zero” Rule
In proto3, the first enum value must always be assigned 0.
This is not arbitrary:
- Zero is the default value for enum fields when no value is explicitly set.
- This ensures backward compatibility and predictable defaults.
- It avoids ambiguity during deserialization when a field is missing.
Because of this rule, developers often use a default like:
BODY_STYLE_UNSPECIFIED = 0;
This makes it clear that the value represents “not set” or “unknown.”
Using Enums Inside a Message
Once the enum is defined, you can include it as a field in a message just like any other type.
Example:
message Car {
string brand = 1;
string model = 2;
BodyStyle body_style = 3;
}
Here:
body_styleis strongly typed.- Only values from
BodyStylecan be assigned. - The default will be
BODY_STYLE_UNSPECIFIEDif not set.
Using Enums in Java
Example usage in Java:
Car car = Car.newBuilder()
.setBrand("Toyota")
.setModel("Camry")
.setBodyStyle(BodyStyle.SEDAN)
.build();
Accessing it:
BodyStyle style = car.getBodyStyle(); System.out.println(style);
Key points:
- The value is handled as an enum, not a string or integer.
- This gives compile-time safety and IDE auto-completion.
Practical Design Tips
- Always include a meaningful zero value such as
UNSPECIFIEDorUNKNOWN. - Avoid renumbering enum values after release because it can break compatibility.
- You may add new values later, but do not reuse old numbers.
- Choose stable names since they become part of your contract.
