Перейти к содержимому
ГоМастер

Поиск по урокам

Найдите урок, курс, термин или раздел

Урок 12 из 25 ~18 мин +50 XP

Switch: множественный выбор

Switch в Go — это множественный выбор в чистом виде: одно выражение сравнивается сразу с набором вариантов, и выполняется первая совпавшая ветка. После урока про [if и else](/courses/go-basics/lessons/11-usloviya-if/) у вас уже есть рабочая лестница else if — теперь разберём, когда её стоит переписать на switch, как перечислить несколько значений в одном case, зачем нужен default, почему в Go нет проваливания веток и что такое type switch.

Содержание урока · 8 разделов

Switch в go: когда if становится лестницей#

Представьте бота, который принимает команды: "start", "stop", "status", "help". Лестницей else if это выглядит как пять проверок одной и той же переменной подряд — пять строк, в которых cmd == повторяется, как заевшая пластинка. Когда одна переменная или одно выражение сравнивается со списком значений, Go предлагает короткую запись — оператор switch, множественный выбор:

switch cmd { case "start": … case "stop": … default: … } — слева слово switch и выражение, дальше фигурные скобки, внутри ветки case со значением и двоеточием, а default ловит всё, что не подошло. Круглых скобок вокруг выражения нет, как и у if. Значения case не обязаны быть константами — подойдёт любое выражение того же типа, что и само выражение switch.

Работает это так же, как лестница else if из прошлого урока: варианты перебираются сверху вниз, и первая же совпавшая ветка выполняется. Разница в форме и в жёсткой гарантии: в switch выполнится ровно одна ветка, без исключений, и при взгляде на код список вариантов виден сразу — как меню.

Песочница: расписание по дням недели

Запустите: при day = 3 печатается «среда», остальные ветки молча пропускаются. Поставьте day = 6 — сработает default, потому что ветки на 6 и 7 ещё не написаны. Допишите их двумя отдельными case со строкой «выходной», запустите, а затем объедините в одну ветку `case 6, 7:` — вывод не изменится, а строк станет меньше.

Вывод

Запустите код, чтобы увидеть вывод.

Как switch выбирает ветку: неявный break и default#

Выражение switch вычисляется один раз, после чего значения case перебираются сверху вниз. Первая ветка, чьё значение совпало, выполняется — и на этом switch заканчивается: нижние ветки не трогаются, даже если их значения тоже совпадают. В конце тела каждой ветки как будто стоит невидимый break — за это свойство отвечают слова «неявный break». В C или Java без явного break выполнение проваливалось бы в следующую ветку, и забытый break годами входил в списки самых дорогих багов; в Go проваливание убрали по умолчанию, а оставили как отдельный оператор — о нём ниже.

Ветка default — это финальный else: она ловит все значения, не подошедшие ни одному case. Писать её можно в любом месте switch, хоть первой, но выполнится она всё равно последней по очереди решения — только если ни один case не совпал. Не больше одного default на switch, и по конвенции его ставят последним, чтобы он читался как подвал списка. А если default нет и ни один вариант не совпал, switch просто пропускается: программа едет дальше, ошибки нет. Для ввода пользователя default почти всегда нужен — «неизвестная команда» это нормальная ситуация, а не сбой.

Проверьте себя: сколько строк выведет switch

n := 7, и перед нами switch n { case 6: печать «суббота»; case 7: печать «воскресенье»; }. Сколько строк напечатает программа?

Case с несколькими значениями и switch по строкам#

Соседние варианты с одинаковым телом не нужно дублировать: в одном case через запятую перечисляется сколько угодно значений — case 6, 7: читается как «если день равен шести или семи». Так же спокойно switch работает со строками, ведь строки в Go сравниваются простым ==, без хитрых функций: команды бота, коды ответов, названия складов. Мы разбирали сравнение строк в уроке про строки и руны — switch просто применяет это сравнение к списку вариантов.

У switch, как и у if, есть инициализирующий оператор: switch n := len(pin); n { — переменная объявляется прямо в заголовке, используется в ветках и умирает после конструкции. Приём стоит держать в голове для «временных» значений, которые нужны только ради выбора: подсчитанная длина, вычисленная цена, округлённый балл.

Одно ограничение стоит запомнить сразу: все значения в ветках должны быть сравнимы с выражением switch по типу. Смешать числа и строки в одном switch нельзя — настоящий компилятор откажется собирать программу со словами о несовпадении типов.

Что выведет код?

Прочитайте код и выберите, что он выведет:

package main

import "fmt"

func main() {
        cmd := "stop"
        switch cmd {
        case "start", "go":
                fmt.Println("запускаю")
        case "stop":
                fmt.Println("останавливаю")
        case "status":
                fmt.Println("всё работает")
        }
}

Песочница: switch без выражения

Запустите: при speed = 72 печатается «в пределах нормы». Поднимите speed до 95 — теперь первая ветка. Добавьте над ней ещё одну: `case speed > 110:` с предупреждением о протоколе — и проверьте 120: сработает самая верхняя, а до соседних дело не дойдёт. Затем возьмите любую лестницу else if из урока про if и перепишите её на switch — сравните, где список условий читается быстрее.

Вывод

Запустите код, чтобы увидеть вывод.

Switch без выражения или else if: что выбрать#

Switch без выражения после ключевого слова — это shorthand для switch true: в ветках пишутся произвольные булевы условия из сравнений, && и || (всё это мы собирали в уроке про bool и логику), и выполняется первая истинная. Получается та же лестница else if, только с выровненным левым краем и без череды if/else if.

Важная деталь: в классическом switch с тегом диапазон записать нельзя. case speed > 90 при числовом speed — попытка сравнить число с bool, компилятор откажет. Поэтому диапазоны живут либо в безвыраженном switch, либо в лестнице if.

Моё правило короткое: одна переменная против списка конкретных значений — switch с тегом; диапазоны и смешанные условия — switch без выражения или if, что из них приятнее глазу. Споры «switch против if» в ревью я обычно пропускаю мимо: конструкции взаимозаменяемы, и тратить на них нитки обсуждения скучно. Но если выбор по значению надо повторять много раз подряд — например, для каждого элемента списка — без циклов уже не обойтись: об этом следующий урок, цикл for.

Шпаргалка: if или switch#

СитуацияЧем пользоватьсяКак выглядит
Одна переменная против набора значенийswitch с выражениемswitch day { case 6, 7: … }
Диапазоны, комбинированные условияswitch без выражения или else ifswitch { case speed > 90: … }
Одно условие, два исходаif / elseif err != nil { … }
Вспомогательная переменная для выбораinit-оператор в заголовкеswitch n := len(s); n { … }
Осознанный провал в следующую веткуfallthrough (редко)case 1: … fallthrough

Проверьте себя: типы в case

speed := 72. Какой из этих switch соберёт настоящий компилятор Go?

Fallthrough: провал в следующую ветку

GO
package main

import "fmt"

func main() {
        n := 1
        switch n {
        case 1:
                fmt.Println("один")
                fallthrough
        case 2:
                fmt.Println("два")
        case 3:
                fmt.Println("три")
        }
}

Пример с оговоркой по движкам: основной движок песочницы — настоящий Go — fallthrough исполняет, и программа напечатает «один» и «два»: fallthrough переносит управление в тело следующей ветки, не проверяя её условие. Кнопки запуска у блока нет: резервный упрощённый движок отвечает «fallthrough не поддерживается в песочнице», а блок обязан корректно работать в любом режиме. Ставить fallthrough можно только последним оператором в теле ветки, а в последней ветке switch он запрещён вовсе. В код-ревью я отношусь к fallthrough подозрительно: почти всегда он означает, что автор хотел case 1, 2: и не заметил, либо действие веток стоит вынести в общую строку ниже. Проваливание веток — источник нашумевших багов в C-подобных языках, и Go отказался от него не случайно.

Type switch: выбор по типу значения

GO
package main

import "fmt"

func describe(x any) string {
        switch v := x.(type) {
        case int:
                return fmt.Sprintf("целое число: %d", v)
        case string:
                return fmt.Sprintf("строка из %d символов", len(v))
        default:
                return fmt.Sprintf("не знаю, что за тип %T", v)
        }
}

func main() {
        fmt.Println(describe(42))
        fmt.Println(describe("secret"))
        fmt.Println(describe(2.5))
}

Тоже пример с оговоркой по движкам: type switch исполняет настоящий Go и основной движок песочницы, а резервный упрощённый отвечает «type switch (x.(type)) не поддерживается в песочнице; используйте type assertion x.(T)» — поэтому блок оставлен без кнопки запуска. Идея такая: x объявлен как any — значение любого типа; выражение x.(type) разрешено только в switch и выбирает ветку по фактическому типу значения. Переменная v, объявленная через := в заголовке, внутри каждой ветки получает тип этой ветки: в int-ветке её можно печатать через %d, в string-ветке — мерить len. В default v остаётся any. Настоящий Go и основной движок песочницы выведут три строки: «целое число: 42», «строка из 6 символов», «не знаю, что за тип float64». Отдельные проверки типов работают в любом движке — type assertion x.(T) с ok-формой, а системно про интерфейсы поговорим в курсе go-advanced.

Вставьте ключевые слова switch

Соберите разбор команд бота: при cmd = "exit" ни одна именованная ветка не подходит, и программа должна попасть в запасную — напечатать «неизвестная команда: exit». Четыре пропуска: слово, открывающее выбор, слово для каждой именованной ветки и слово для запасной.

cmd := "exit" cmd { "help": fmt.Println("команды: help, start, stop") "start": fmt.Println("запускаю") : fmt.Println("неизвестная команда:", cmd) }

Соберите программу продажи билетов

Соберите switch по зоне зала: зона «B» должна попасть в общий case с зоной «A» и напечатать «партер: 3000 руб.». Строки идут в порядке: шапка программы, переменная, сам switch с ветками и две закрывающие скобки.

Строки

Ваша программа

Нажимайте на строки слева, чтобы собрать программу

Карточки: switch и множественный выбор

Термин

Зачем switch, если есть if/else?

нажмите, чтобы перевернуть
Определение

Для множественного выбора: одно выражение сравнивается с набором вариантов сразу, запись короче и читается как меню. Гарантированно выполняется ровно одна ветка — первая совпавшая.

1 / 6

Песочница урока

Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.

Вывод

Запустите код, чтобы увидеть вывод.

Коротко: что запомнить

  • Switch — множественный выбор: выражение вычисляется один раз, выполняется первая совпавшая ветка, остальные пропускаются даже при совпадении.
  • Break в конце case не нужен — он встроен в язык; осознанный провал вниз пишут через fallthrough, и почти всегда вместо него читается запятая в case.
  • Несколько значений перечисляются через запятую: case 6, 7: — срабатывает при любом из них.
  • default ловит всё остальное, может стоять в любом месте (по конвенции — последним) и не выполняется, когда совпал case.
  • switch без выражения — это switch true: в ветках булевы условия, побеждает первая истинная проверка.
  • В case с тегом не пишут диапазоны: case speed > 90 при числовом speed — ошибка несовпадения типов; для диапазонов — безвыраженный switch или if.
  • Type switch switch v := x.(type) выбирает ветку по фактическому типу значения; в песочнице не исполняется, type assertion — доступен.

Домашнее задание

Возьмите кассу кинотеатра из домашки прошлого урока и перепишите её на switch без выражения: age := 15, price := 250; до 12 включительно или от 65 — «цена: 125», от 13 до 18 — «цена: 200», остальным — «цена: 250». Проверьте три возраста: 15, 70 и 30. Затем добавьте классический switch по строке film := "night": case "day", "kids" — «билет 200 руб.», case "night" — «билет 350 руб.», default — «сеанс не найден». Проверьте все три значения и одно выдуманное.

Личные заметкиПусто
Хранятся только в вашем браузере (Go Base · Switch: множественный выбор)

Вопросы и ответы по уроку

Чем switch в Go отличается от switch в C или Java?

В C и Java без break выполнение проваливается в следующую ветку, и забытый break — классический источник багов. В Go проваливания по умолчанию нет: ветка завершает switch сама, а осознанный провал пишут словом fallthrough. Ещё в Go можно перечислять несколько значений в одном case и вообще опускать выражение после switch.

Обязателен ли default в switch?

Нет. Если ни один case не совпал и default нет, switch просто пропускается без ошибки. Но там, где приходит ввод пользователя, default почти всегда стоит: «неизвестная команда» или «такого дня нет» — это ожидаемая ситуация, которую надо обработать, а не игнорировать.

Можно ли использовать в case переменные, а не константы?

Да: значения case — любые выражения совместимого типа, константность не требуется. Главное, чтобы все значения были сравнимы с выражением switch: смешать int и string в одном switch нельзя, компилятор отклонит программу из-за несовпадения типов.

Термины урока

Ключевые понятия из этого урока — короткие определения в глоссарии. Наведите на термин, чтобы вспомнить, откройте карточку для подробностей и ссылки на практику.

Следующая ступень учебного пути

Go Pro25 уроков~14 чпрактика25 уроков уже доступны

Продвинутые возможности Go: от замыканий до рефлексии

Продвинутый курс Go: замыкания, defer, горутины, каналы, контекст, интерфейсы, дженерики, рефлексия, тесты и JSON. 25 уроков с практикой в браузере.

  • Писать и читать конкурентный код: go-функции, каналы, select
  • Управлять жизненным циклом операций через context
  • Проектировать API на интерфейсах
  • Использовать дженерики без злоупотребления