Руководство по Стилю

Предоставляет рекомендации по наилучшей структуре ваших proto-определений.

Этот документ предоставляет руководство по стилю для файлов .proto. Следуя этим соглашениям, вы сделаете свои определения сообщений буфера протокола и соответствующие классы последовательными и удобочитаемыми.

Применение следующих стилистических рекомендаций контролируется через enforce_naming_style.

Стандартное форматирование файлов

  • Длина строки должна быть не более 80 символов.
  • Используйте отступ в 2 пробела.
  • Предпочитайте использование двойных кавычек для строк.

Структура файла

Файлы должны называться lower_snake_case.proto.

Все файлы должны быть упорядочены следующим образом:

  1. Заголовок лицензии (если применимо)
  2. Обзор файла
  3. Синтаксис
  4. Пакет
  5. Импорты (отсортированные)
  6. Опции файла
  7. Все остальное

Стили именования идентификаторов

Идентификаторы Protobuf используют один из следующих стилей именования:

  1. TitleCase (ВерблюжийРегистр)
    • Содержит заглавные буквы, строчные буквы и цифры
    • Первый символ - заглавная буква
    • Первая буква каждого слова заглавная
  2. lower_snake_case (нижний_змеиный_регистр)
    • Содержит строчные буквы, подчеркивания и цифры
    • Слова разделяются одним подчеркиванием
  3. UPPER_SNAKE_CASE (ВЕРХНИЙ_ЗМЕИНЫЙ_РЕГИСТР)
    • Содержит заглавные буквы, подчеркивания и цифры
    • Слова разделяются одним подчеркиванием
  4. camelCase (верблюжийРегистр)
    • Содержит заглавные буквы, строчные буквы и цифры
    • Первый символ - строчная буква
    • Первая буква каждого последующего слова заглавная
    • Примечание: Руководство по стилю ниже не использует camelCase для каких-либо идентификаторов в файлах .proto; терминология уточняется здесь только потому, что сгенерированный код некоторых языков может преобразовывать идентификаторы в этот стиль.

Во всех случаях рассматривайте аббревиатуры как отдельные слова: используйте GetDnsRequest вместо GetDNSRequest, dns_request вместо d_n_s_request.

Подчеркивания в идентификаторах

Не используйте подчеркивания в качестве начального или конечного символа имени. Любое подчеркивание всегда должно следовать за буквой (а не цифрой или вторым подчеркиванием).

Мотивация этого правила в том, что каждая реализация языка protobuf может преобразовывать идентификаторы в локальный стиль языка: имя song_id в файле .proto может в итоге иметь аксессоры для поля, которые капитализируются как SongId, songId или song_id в зависимости от языка.

Используя подчеркивания только перед буквами, мы избегаем ситуаций, когда имена могут различаться в одном стиле, но конфликтовать после преобразования в другой стиль.

Например, и DNS2, и DNS_2 преобразуются в TitleCase как Dns2. Разрешение любого из этих имен может привести к болезненным ситуациям, когда сообщение используется только в некоторых языках, где сгенерированный код сохраняет исходный стиль UPPER_SNAKE_CASE, широко устанавливается, и затем используется только позже в языке, где имена преобразуются в TitleCase, где они конфликтуют.

При применении этого правила стиля вы должны использовать XYZ2 или XYZ_V2 вместо XYZ_2 или XYZ_2V.

Пакеты

Используйте имена пакетов в lower_snake_case, разделенные точками.

Многословные имена пакетов могут быть в lower_snake_case или dot.delimited (имена пакетов, разделенные точками, создаются как вложенные пакеты/пространства имен в большинстве языков).

Имена пакетов должны пытаться быть короткими, но уникальными именами, основанными на имени проекта. Имена пакетов не должны быть пакетами Java (com.x.y); вместо этого используйте x.y как пакет и используйте опцию java_package по мере необходимости.

Имена сообщений

Используйте TitleCase для имен сообщений.

message SongRequest {
}

Имена полей

Используйте snake_case для имен полей, включая расширения.

Используйте имена во множественном числе для повторяющихся полей.

string song_name = 1;
repeated Song songs = 2;

Имена oneof

Используйте lower_snake_case для имен oneof.

oneof song_id {
  string song_human_readable_id = 1;
  int64 song_machine_id = 2;
}

Перечисления

Используйте TitleCase для имен типов перечислений.

Используйте UPPER_SNAKE_CASE для имен значений перечислений.

enum FooBar {
  FOO_BAR_UNSPECIFIED = 0;
  FOO_BAR_FIRST_VALUE = 1;
  FOO_BAR_SECOND_VALUE = 2;
}

Первое указанное значение должно быть нулевым значением перечисления и иметь суффикс _UNSPECIFIED или _UNKNOWN. Это значение может использоваться как неизвестное/значение по умолчанию и должно отличаться от любых семантических значений, которые вы ожидаете явно установить. Для получения дополнительной информации о неуказанном значении перечисления см. страницу Лучших практик Proto.

Префиксы значений перечисления

Значения перечисления семантически считаются не ограниченными областью видимости их содержащего имени перечисления, поэтому одно и то же имя в двух родственных перечислениях не допускается. Например, следующее будет отклонено protoc, поскольку значение SET, определенное в двух перечислениях, считается находящимся в одной области видимости:

enum CollectionType {
  COLLECTION_TYPE_UNSPECIFIED = 0;
  SET = 1;
  MAP = 2;
  ARRAY = 3;
}

// Не скомпилируется - имя enum `SET` будет конфликтовать
// с определенным в enum `CollectionType`.
enum TennisVictoryType {
  TENNIS_VICTORY_TYPE_UNSPECIFIED = 0;
  GAME = 1;
  SET = 2;
  MATCH = 3;
}

Риск конфликтов имен высок, когда перечисления определены на верхнем уровне файла (не вложены в определение сообщения); в этом случае родственные элементы включают перечисления, определенные в других файлах, которые устанавливают тот же пакет, где protoc может быть не в состоянии обнаружить конфликт во время генерации кода.

Чтобы избежать этих рисков, настоятельно рекомендуется сделать одно из следующего:

  • Добавлять префикс каждого значения с именем перечисления (преобразованным в UPPER_SNAKE_CASE)
  • Вложить перечисление внутрь содержащего сообщения

Любой вариант достаточен для снижения рисков конфликтов, но предпочитайте перечисления верхнего уровня с префиксными значениями над созданием сообщения просто для смягчения проблемы. Поскольку некоторые языки не поддерживают определение перечисления внутри типа "struct", предпочтение префиксных значений обеспечивает последовательный подход во всех языках привязок.

Сервисы

Используйте TitleCase для имен сервисов и методов.

service FooService {
  rpc GetSomething(GetSomethingRequest) returns (GetSomethingResponse);
  rpc ListSomething(ListSomethingRequest) returns (ListSomethingResponse);
}

Чего следует избегать

Обязательные поля

Обязательные поля - это способ обеспечить, чтобы данное поле было установлено при разборе байтов провода, и в противном случае отказаться от разбора сообщения. Инвариант required обычно не применяется к сообщениям, созданным в памяти. Обязательные поля были удалены в proto3. Поля required proto2, которые были мигрированы в редакции 2023, могут использовать функцию field_presence, установленную в LEGACY_REQUIRED, чтобы приспособиться.

Хотя принудительное применение обязательных полей на уровне схемы интуитивно желательно, одной из основных целей проектирования protobuf является поддержка долгосрочной эволюции схемы. Независимо от того, насколько очевидно обязательным кажется данное поле сегодня, существует правдоподобное будущее, в котором поле больше не должно устанавливаться (например, int64 user_id может потребоваться мигрировать на UserId user_id в будущем).

Особенно в случае промежуточных серверов, которые могут пересылать сообщения, которые им на самом деле не нужно обрабатывать, семантика required оказалась слишком вредной для этих долгосрочных целей эволюции, и поэтому теперь очень настоятельно не рекомендуется.

См. Required является строго устаревшим.

Группы

Группы - это альтернативный синтаксис и формат провода для вложенных сообщений. Группы считаются устаревшими в proto2, были удалены из proto3 и преобразуются в разделяемое представление в редакции 2023. Вы можете использовать определение вложенного сообщения и поле этого типа вместо использования синтаксиса группы, используя функцию message_encoding для совместимости с проводом.

См. groups.