Extension Declarations

Describes in detail what extension declarations are, why we need them, and how we use them.

Introduction

This page describes in detail what extension declarations are, why we need them, and how we use them.

If you need an introduction to extensions, read this extensions guide

Motivation

Extension declarations aim to strike a happy medium between regular fields and extensions. Like extensions, they avoid creating a dependency on the message type of the field, which therefore results in a leaner build graph and smaller binaries in environments where unused messages are difficult or impossible to strip. Like regular fields, the field name/number appear in the enclosing message, which makes it easier to avoid conflicts and see a convenient listing of what fields are declared.

Listing the occupied extension numbers with extension declarations makes it easier for users to pick an available extension number and to avoid conflicts.

Usage

Extension declarations are an option of extension ranges. Like forward declarations in C++, you can declare the field type, field name, and cardinality (singular or repeated) of an extension field without importing the .proto file containing the full extension definition:

edition = "2023";

message Foo {
  extensions 4 to 1000 [
    declaration = {
      number: 4,
      full_name: ".my.package.event_annotations",
      type: ".logs.proto.ValidationAnnotations",
      repeated: true },
    declaration = {
      number: 999,
      full_name: ".foo.package.bar",
      type: "int32"}];
}

This syntax has the following semantics:

  • Multiple declarations with distinct extension numbers can be defined in a single extension range if the size of the range allows.
  • If there is any declaration for the extension range, all extensions of the range must also be declared. This prevents non-declared extensions from being added, and enforces that any new extensions use declarations for the range.
  • The given message type (.logs.proto.ValidationAnnotations) does not need to have been previously defined or imported. We check only that it is a valid name that could potentially be defined in another .proto file.
  • When this or another .proto file defines an extension of this message (Foo) with this name or number, we enforce that the number, type, and full name of the extension match what is forward-declared here.

The extension declarations expect two extension fields with different packages:

package my.package;
extend Foo {
  repeated logs.proto.ValidationAnnotations event_annotations = 4;
}
package foo.package;
extend Foo {
  optional int32 bar = 999;
}

Reserved Declarations

An extension declaration can be marked reserved: true to indicate that it is no longer actively used and the extension definition has been deleted. Do not delete the extension declaration or edit its type or full_name value.

This reserved tag is separate from the reserved keyword for regular fields and does not require breaking up the extension range.

edition = "2023";