Метки в го используются в операторах continue, break как необязательны парамеры и как обязательный аргумент для оператора goto.
Предельная область видимости метки ограничивается областью видимости функции в которой она обозначена.
2.1. Метки для switch
2.2. Метки для select
2.3. Метки для for
1. Метка для goto
Знакомый многим оператор безусловного перехода goto имеет следующую форму:
func main() { fmt.Printf("%d", 1) goto goToMe return goToMe: fmt.Printf("%d", 0) }
После выполнения кода мы получим 10 в стандартном выводе. Если бы не goto, о мы бы получили ошибку об неиспользуемом коде после return. Go достаточно умён и это хорошо.
Меткой тут является goToMe. Метки в го не нужно описывать в разделе меток, как это делается в паскале. Её достаточно объявить.
Важно понимать область видимости. Если мы объявим метку в отдельном блоке (фигурные скобки, цикл), то мы не сможем перейти к нему, но go нам об этом скажет.
func main() { fmt.Printf("%d", 1) goto goToMe return { goToMe: fmt.Printf("%d", 0) } }
Данный код не будет собран так как безусловный переход не видит метку.
Обычно использование goto считается плохим тоном, но в go его можно встретить в стандартной библиотеке, что развязывает руки для любителей извращений.
Но также не получится объявить метку с одним именем внутри вложенного блока, а именно следующий код не соберётся уже по другой причине. По причине того, что метка уже определена.
func main() { fmt.Printf("%d", 1) goto goToMe return goToMe: { goto goToMe return goToMe: fmt.Printf("%d", 0) } }
2. Метки для continue, break
Также метки позволяют расширить возможности операторов continue, break, которые в свою очередь можно использовать в конструкциях switch, for и select.
Разберём каждый случай отдельно.
2.1. Метки для switch
Мы знаем, что в отличие от некоторых других языков в go не нужно прерывать оператор switch после успешного условия в case, чтобы код не пошёл по
кейсам дальше. В го выполнение закончится само, но остановку case через метку можно встретить в стандартной библиотеке. Пакет encoding/json (актуально для go 1.18)
Так что опишем как это делается, а делается просто.
func main() { i := 1 breakLabel: switch i { case 1: fmt.Println(1) if i == 1 { break breakLabel } fallthrough case 2: fmt.Println(2) } }
Пришлось обернуть break в условный оператор, чтобы умный го не ругался. fallthrough должен был повторить поведение некоторых других языков
и отправить исполнение в следующий case. Но этого не происходит так как мы решили прервать switch.
2.2. Метки для select
Метки для select работают идентично меткам для switch. Так что смотрим пункт выше.
2.3. Метки для for
Метки для for на мой взгляд более практичны, фактически они позволяют выйти из нескольких циклов сразу, что бывает полезно.
func main() { LoppForI: for i := 0; i < 10; i++ { for j := 0; j < 10; j++ { fmt.Printf("%d, %d\n", i, j) break LoppForI } } }
В выводе мы получим "0, 0" и всё.
2.4. Комбинирование меток для select, swich, for.
Можно объявить метку для switch, внутрь положить select, внутрь for и вызывать break можно будет из любого места, где мы можем её увидеть.
Именно такой формат прерывания используется в encoding/json. Внутри switch есть два цикла, которые прерываются по определённым условиям для того чтобы не создавать
метки для каждого цикла, есть одна метка на switch. Удобно.
3. Метки для continue
Позволяют пропустить все оставшиеся итерации внутреннего цикла и перейти сразу к следующей итерации внешнего цикла. Всё просто.
func main() { continueLoop: for i := 0; i < 2; i++ { for j := 0; j < 10; j++ { fmt.Printf("%d, %d\t", i, j) continue continueLoop } } }
Вывод программы будет следующим "0, 0 1, 0". Других применений не знаю.
На этом всё.