Menu

Константы в Golang: const, типизированные и нетипизированные

Как работает const в Go: объявление констант, разница между типизированными и нетипизированными константами, константные выражения с произвольной точностью и почему в Go нет константных слайсов, мап и структур.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Константа это значение, зафиксированное на этапе компиляции. Объявите её через const, и Go отвергнет любую попытку её изменить.

Константы можно объявлять на уровне пакета или внутри функции, по одной на строку или группой в блоке const ( ... ). MB и GB вычисляются из других констант, и компилятор выполняет эту арифметику один раз, при сборке.

Присваивание константе, например MaxRetries = 5, это ошибка компиляции:

./main.go:8:2: cannot assign to MaxRetries (neither addressable nor a map index expression)

Что может быть константой

Только три вида значений: булевы, числа (целые, с плавающей точкой, комплексные и руны) и строки. Значение должен уметь вычислить компилятор: литералы, другие константы, арифметика над ними и несколько встроенных функций вроде len от константной строки.

Всё, что требует выполнения кода или выделения памяти, исключено:

const Colors = []string{"red", "green"} // slice
const Started = time.Now()              // function call
./main.go:9:16: []string{…} (value of type []string) is not constant
./main.go:10:13: time.Now() (value of struct type time.Time) is not constant

Почему в Go нет константных слайсов и мап

Слайсы, мапы и содержащие их структуры это ссылки на память, которая существует во время выполнения, поэтому компилятор не может вшить их в программу как фиксированные значения, а модификаторов readonly или final для переменных в Go нет. Типичные обходные пути:

Копия стоит одной аллокации на вызов, и для данных размером с конфигурацию это нормально. Для поиска по ключу есть ещё один вариант, похожий на константу: switch внутри функции, func statusText(code int) string { switch code { ... } }.

Нетипизированные константы

Константа, объявленная без типа, называется нетипизированной. У неё есть вид (целое, число с плавающей точкой, руна, строка, bool), но нет конкретного типа Go, пока она не использована, и она подстраивается под контекст:

Ratio работает как int, float64, uint8 и float32 без всякого преобразования. Поэтому и компилируется time.Sleep(2 * time.Second): 2 нетипизирована и становится time.Duration.

Когда контекст не требует типа, как в x := Ratio, константа получает тип по умолчанию:

Вид нетипизированной константыТип по умолчанию
целое (42)int
с плавающей точкой (4.2)float64
руна ('a')rune (int32)
комплексное (2i)complex128
строкаstring
булевоbool

Точная арифметика

Нетипизированные числовые константы точны. Компилятор представляет целые числа как минимум 256 битами, поэтому промежуточные значения могут быть намного больше любого типа Go:

Саму Huge нельзя напечатать как целое число: при передаче в fmt.Println она преобразуется в int, и компилятор это ловит (float64(Huge) работает, потому что float вмещает такую величину):

cannot use Huge (untyped int constant 1267650600228229401496703205376) as int value in argument to fmt.Println (overflows)

Та же проверка на этапе компиляции останавливает и ошибки поменьше, например var b byte = 300:

cannot use 300 (untyped int constant) as byte value in variable declaration (overflows)

Типизированные константы

Дайте константе тип, и она везде ведёт себя как значение этого типа, включая отказ Go смешивать типы:

const Limit int = 10

var f float64 = 2
fmt.Println(Limit * f)
invalid operation: Limit * f (mismatched types int and float64)

С const Limit = 10 (нетипизированной) выражение Limit * f компилируется и даёт 20. Поэтому оставляйте константы нетипизированными, если тип не несёт смысла. Смысл он несёт, когда вы объявляете константы собственного именованного типа; так в Go строятся перечисления:

type Weekday int

const (
	Sunday Weekday = iota
	Monday
	Tuesday
)

Здесь тип и есть суть: функция, принимающая Weekday, сама документирует, что ей нужно. iota и шаблоны перечислений на его основе разобраны на странице про enum и iota.

const и var

constvar
Может менятьсянетда
Значение известнона этапе компиляцииво время выполнения
Допустимые типыbool, числовые, stringлюбые
Занимает память во время выполнениянетда
Можно взять адрес (&x)нетда
Неиспользуемая это ошибканетда, для локальных

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

Подводные камни

Типизированные константы не подстраиваются. const Timeout int = 5 нельзя передать туда, где ожидается time.Duration, без преобразования; нетипизированную const Timeout = 5 можно умножить: Timeout * time.Second.

Деление целочисленных констант целочисленное. const Half = 1 / 2 равна 0, потому что оба операнда нетипизированные целые. Напишите 1.0 / 2, чтобы получить 0.5.

Нет адреса. &MaxRetries не компилируется. Если API нужен *int, сначала скопируйте константу в переменную.

Часто задаваемые вопросы

Как объявить константу в Go?

Через const: const MaxRetries = 3 или с явным типом: const Timeout time.Duration = 5 * time.Second. Несколько констант можно сгруппировать в блок: const ( A = 1; B = 2 ). Значение должно вычисляться на этапе компиляции.

Можно ли сделать константный слайс, мапу или массив в Go?

Нет. Константами могут быть только булевы значения, числа (включая руны) и строки. const Colors = []string{"red"} не компилируется с ошибкой is not constant. Используйте var на уровне пакета, а если вызывающий код не должен его менять, отдавайте наружу функцию, которая возвращает свежую копию.

Что такое нетипизированная константа в Go?

Константа, объявленная без типа, например const Pi = 3.14159. У неё нет фиксированного типа, пока она не использована, поэтому одна и та же константа работает как float32, как float64 или, если позволяет значение, как int. Нетипизированные константы ещё и точные: компилятор хранит не меньше 256 бит точности, поэтому const Big = 1 << 100 допустима, если использовать её только в выражениях, результат которых помещается в тип.

Чем const отличается от var в Go?

const фиксируется на этапе компиляции и никогда не меняется; во время выполнения она не занимает память и может хранить только bool, число или строку. var это ячейка памяти, которую можно переприсвоить, и она может хранить любой тип, в том числе значения, вычисленные во время выполнения, например time.Now().

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ