В ситуациях, когда ход работы программы должен меняться в зависимости от условия, применяют операторы ветвления, или, как их ещё называют, условные операторы. В языке Go есть как стандартные условные операторы, так и специфичные для языка.

Для начала вспомним операторы сравнения (переменных одного типа):

  • > — больше;
  • < — меньше;
  • >= — больше или равно;
  • <= — меньше или равно;
  • == — равно;
  • != — не равно.

А также логические операторы:

  • && — логическое И;
  • || — логическое ИЛИ;
  • ! — логическое НЕ.

Теперь рассмотрим конструкции if — else и switch — case. Первая конструкция — стандартный условный оператор. Вторая — более специфичная, встречается не во всех языках программирования и применяется реже.

Условие if — else

if a == 1 {
    // сценарий, если условие if выполнено
} else if a == 2 {
    // сценарий, если условие else if выполнено
} else {
    // сценарий, если условие else if не выполнено
} 

Это простой пример условной конструкции. Она может состоять только из одного оператора if — тогда блок кода сработает, если условие верно, а если неверно, то ничего не произойдёт.

Опционально можно добавить оператор else. За ним будет следовать блок кода, который выполнится, если исходное условие неверно.

И наконец, если нужно обработать несколько различных условий, используют оператор else if. В код можно добавить много else if, но это не самый «читабельный» вариант — в подобных случаях лучше использовать case, о котором расскажем ниже.

Приведём примеры операторов с различными вариантами условий:

// логическое НЕ
// возвращается одна переменная типа bool
a := false
if !a {} 
 
// логическое И
var a, b int
if a == 1 && b == 2 {}
 
// исключающее ИЛИ (XOR)
var a, b bool
if (a || b) && !(a && b) {}

Общие правила для условных операторов:

  1. Обязательно использовать фигурные скобки { }, чтобы обозначить область видимости оператора.
  2. Необязательно заключать основное условие в круглые скобки ( ), но с ними удобнее читать код.
  3. Можно добавлять круглые скобки ( ), чтобы группировать части условия.

В#Go применяется «ленивая» проверка условий: она идёт слева направо до первого false и прекращается, потому что проверять дальше нет смысла. Пример «ленивой» проверки:

a, b := 1, 0
 
if a == 1 || b == 2 {
    fmt.Println("Hello")
} 

В данном примере выполняется левое условие, поэтому проверки (или выполнения) правой части не последует. Пример будет более показателен, если для правой части условия использовать функциональный литерал (подробнее расскажем в теме «Функции»), который будет просто изменять значение переменной b.

a, b := 1, 0
 
incB := func() bool {
    b = b + 1
    return true
}
 
if a == 1 || incB() {
    fmt.Println("Hello")
}
 
fmt.Println(a, b)

Из-за «ленивой» проверки условий функция incB не выполнится — её значение не изменится, то есть функция не изменит значение переменной, потому что выполнение кода прервётся.

Оператор if может состоять из двух компонент: инициализации и основного условия. Такая техника позволяет объявлять локальную переменную, которую используют только в рамках области видимости if. Это может пригодиться, например, когда нужно преобразовать данные для сравнения.

a := 0.10000001 // float64
// инициализация и основное условие
if b := float32(a); b > float32(0.1) {
    fmt.Println("Var a is GT float32(0.1)")
} 

Условие в данном примере может перестать выполняться, если добавить ещё один ноль: 0.100000001. Тип float32 обеспечивает точность в восемь десятичных чисел, в то время как точность float64 составляет около 15 чисел.

Условие switch — case

var a int
 
switch a {
case 1:
    fmt.Println("1")
case 2:
    fmt.Println("2")
case 3, 4:
    fmt.Println("3 or 4")
default:
    fmt.Println("Default case")
} 

Конструкция switch — case позволяет избежать дублирования else if. Проверка условий идёт сверху вниз и слева направо, поэтому в примере выше условия будут проверяться в таком порядке: 1, 2, 3, 4.

Наличие блока default необязательно — его можно опустить, если не требуется описывать «стандартное» поведение. Этот блок выполнится, если ни одно из условий не отработало.

Основное условие switch может быть не задано явно:

var a int
 
switch {
case a == 100:
    fmt.Println("EQ 100")
case a > 0:
    fmt.Println("GT 0 AND NEQ 100")
case a < 0:
    fmt.Println("LT 0 AND NEQ 100")
} 

В этом примере важен порядок условий case: если переместить a == 100 в конец, то a > 0 всегда будет срабатывать первым для положительных чисел. Такая форма оператора аналогична множественному else if.

Внутри switch можно объявить локальную переменную, доступную только в пределах области видимости оператора:

a := 6
switch b := a % 5; {
case b == 0:
    fmt.Println("Кратно 5")
default:
    fmt.Printf("Остаток от деления на 5: %d", b)
} 

Чтобы досрочно прервать выполнение case, используют ключевое слово break. Это бывает полезно, когда внутри case есть условные конструкции. В Go нет необходимости явно указывать break в конце каждого case, так как следующий блок case автоматически не выполнится при совпадении условия.

Когда нужно всё-таки выполнить следующий блок, используют ключевое слово fallthrough. Если указать его в конце блока кода, то после него будет выполнен блок в следующем case или default.

a := -100
switch {
case a > 0:
    if a % 2 == 0 {
        break
    }
    fmt.Println("Odd positive value received")
case a < 0:
    fmt.Println("Negative value received")
    fallthrough
default:
    fmt.Println("Default value handling")
} 

У ключевого слова fallthrough есть особенности:

  • его можно использовать только в последней строке case, иначе будет ошибка компиляции;
  • оно игнорирует условие следующего по порядку case.

📂 Go | Последнее изменение: 24.08.2024 10:35