Помимо применения к различным вещам (правило № 5 предназначено для констант- и объявлений переменных , правило № 6 предназначено для объявлений типов ), существует такжеважное отличие в формулировке:
Область действия идентификатора константы или переменной, объявленного внутри функции , начинается в конце ConstSpec или VarSpec (ShortVarDecl для коротких объявлений переменных) и заканчивается в конце самого внутреннего содержащего блока. Область действия идентификатора типа, объявленного внутри функции , начинается с идентификатора в TypeSpec и заканчивается в конце самого внутреннего содержащего блока.
По этой причине существует два правила, а не одно.
Что это значит?Что означает это различие?
# 5 Объявления переменных и констант (внутри функции)
Область действия объявленных переменных или констант начинается в конце объявления.Это означает, что если вы создаете переменную функции, инициализируя ее анонимной функцией, она не может ссылаться на себя.
Это недопустимо:
f := func() {
f()
}
Попытка компиляции:
prog.go:5:3: undefined: f
Это потому, что объявление заканчивается после закрывающей скобки анонимной функции, поэтому внутри нее вы не можете вызвать f()
.Обходной путь может быть следующим:
var f func()
f = func() {
f()
}
Теперь здесь объявление f
заканчивается закрывающей скобкой (типа func()
), поэтому на следующей строке, когда мы назначаем ей анонимную функцию,допустимо ссылаться на f
(для вызова значения функции, хранящегося в переменной f
), потому что теперь оно находится в области видимости.См. Связанный вопрос: Определение рекурсивной функции внутри функции в Go
Аналогично, при инициализации переменной, например, с помощью составного литерала , вы не можете обратиться кпеременная внутри него:
var m = map[int]string{
1: "one",
21: "twenty-" + m[1],
}
Это дает ошибку времени компиляции ("undefined: m"), потому что m
еще не находится в области видимости внутри составного литерала.
Иочевидно, этот обходной путь работает:
var m = map[int]string{
1: "one",
}
m[21] = "twenty-" + m[1]
# 6 Объявления типов (внутри функции)
Область действия объявленного типа начинается с идентификатора в объявлении.Таким образом, в отличие от правила № 5, допустимо ссылаться на сам тип внутри его объявления.
Имеет ли он какое-либо преимущество / значение?
Да, вы можете объявлять рекурсивные типы, напримеркак это:
type Node struct {
Left, Right *Node
}
Идентификатор типа Node
находится в области видимости сразу после его появления в объявлении типа, которое заканчивается закрывающей скобкой, но до этого мы могли бы ссылаться на него со смыслом.
Другим примером может быть тип слайса, чей тип элемента сам по себе:
type Foo []Foo
Подробнее об этом можно прочитать здесь: Как фрагмент может содержать сам себя?
Еще один действительный пример:
type M map[int]M