Avatar ·

Соглашения по именованию констант в Go: CamelCase, ALL_CAPS или что-то ещё?

Всем привет! Я только начал изучать Go после Python и C#, и меня немного смущают соглашения по именованию констант. В Python мы обычно используем `ALL_CAPS` для констант: `MAX_CONNECTIONS = 100`. В C# принято использовать `PascalCase`. А в Go я вижу разный код в разных проектах. Например, в стандартной библиотеке Go я наткнулся на такие примеры: ```go // В пакете http const ( MethodGet = "GET" MethodPost = "POST" ) // В пакете os const ( O_RDONLY = 0x0 O_WRONLY = 0x1 ) ``` Первый пример использует CamelCase (`MethodGet`), а второй — snake_case с заглавными буквами (`O_RDONLY`). Когда я спросил коллегу, он сказал, что в Go нужно использовать `CamelCase` для экспортируемых констант и `lowerCamelCase` для неэкспортируемых. Но тогда почему в `os` используется `O_RDONLY`? Это же экспортируемая константа! Я написал свой код так: ```go package config const MAX_RETRIES = 3 const DEFAULT_TIMEOUT_SECONDS = 30 const DATABASE_URL = "localhost:5432" ``` Но линтер выдает предупреждение, что имена должны быть в CamelCase. Я запутался. Подскажите, пожалуйста, какие правила именования констант в Go действительно правильные? Когда нужно использовать ALL_CAPS, а когда CamelCase? Есть ли какие-то исключения? <<>>

Основное правило: CamelCase, а не ALL_CAPS

В Go действует простое правило: используйте CamelCase для всех констант, как и для любых других экспортируемых (публичных) идентификаторов. Привычка из других языков писать константы заглавными буквами с подчёркиваниями (ALL_CAPS) в Go не приветствуется.

Вот правильный пример для вашего случая:

package config

const MaxRetries = 3
const DefaultTimeoutSeconds = 30
const DatabaseURL = "localhost:5432"

Для неэкспортируемых (приватных) констант используйте lowerCamelCase:

const maxRetries = 3
const defaultTimeout = 30

Исключения: когда используют ALL_CAPS

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

  • Константы из C-шного наследия — например, O_RDONLY, O_WRONLY, O_CREAT из пакета os. Они были перенесены из Unix/POSIX API, где такие имена уже устоялись десятилетиями. Это исключение, а не правило.
  • Константы для масок и битовых флагов — иногда используют FlagA, FlagB в CamelCase, но если они часть устоявшегося C-стиля, могут остаться в ALL_CAPS.
  • Константы, имитирующие системные вызовы — например, SIGINT, SIGTERM в пакете os/signal. Это тоже дань историческому контексту.

Важно понимать: для новых проектов и новых констант используйте CamelCase. Исключения оправданы только когда вы напрямую работаете с унаследованными C-интерфейсами или системными вызовами.

Почему так, а не иначе?

В Go дизайн языка намеренно избегает "визуального шума". ALL_CAPS в Go считается менее читаемым, особенно когда констант много. Правило простое: экспортируемое = с большой буквы, неэкспортируемое = с маленькой. И это относится ко всем идентификаторам: функциям, типам, переменным, константам.

Если вы пишете библиотеку или приложение с нуля, забудьте про ALL_CAPS. Используйте:

const (
    // Экспортируемые
    MaxConnections = 100
    DefaultPort    = 8080

    // Неэкспортируемые
    internalRetryLimit = 3
    secretKey          = "supersecret"
)

Так ваш код будет соответствовать официальным рекомендациям Effective Go и проходить линтеры (например, golint или revive) без предупреждений.

Дополнительный совет: группировка констант

Используйте const с блоком для логической группировки, как в примере выше. Это улучшает читаемость и позволяет использовать iota для перечислений:

type Status int

const (
    StatusActive   Status = iota // 0
    StatusInactive               // 1
    StatusPending                // 2
)

В этом случае имена тоже в CamelCase, а не в ALL_CAPS.

<<>>

Альтернативный взгляд: ALL_CAPS для констант-значений из внешних систем

Хотя основной ответ абсолютно корректен, стоит добавить важное дополнение. В некоторых проектах (особенно при работе с конфигурацией, переменными окружения или внешними API) может быть разумно использовать ALL_CAPS для констант, которые напрямую соответствуют внешним именам.

Пример: если вы работаете с переменными окружения:

const (
    EnvDatabaseURL  = "DATABASE_URL"    // имя переменной окружения
    EnvAPIKey       = "API_KEY"
    EnvLogLevel     = "LOG_LEVEL"
)

Здесь ALL_CAPS оправдан, потому что константа хранит имя внешнего идентификатора, который сам по себе в ALL_CAPS. Это помогает при поиске и не вводит в заблуждение.

Другой случай — константы для HTTP-заголовков или кодов ошибок из RFC:

const (
    HeaderContentType     = "Content-Type"     // CamelCase — имя заголовка
    HeaderAuthorization   = "Authorization"
    StatusOK             = 200
    StatusNotFound       = 404
)

Здесь CamelCase всё ещё предпочтительнее, потому что так принято в HTTP-спецификациях. Но если вы работаете с унаследованным кодом на C или системными вызовами, ALL_CAPS может быть допустимым локальным соглашением для конкретного пакета.

Главный принцип: будьте последовательны в рамках одного пакета или проекта. Если в вашем проекте уже используется CamelCase (что рекомендуется), не вводите ALL_CAPS без веской причины. А если работаете с существующей кодовой базой, следуйте её стилю — линтер можно настроить или отключить для конкретных файлов через комментарии //nolint.