Условия, циклы и блоки

Осваиваем поток управления: ветвления, циклы и switch без сюрпризов.

Блок в Zig — это выражение: он может возвращать значение через break :label value, поэтому многие конструкции пишутся компактно и без временных переменных.

Управляющие конструкции Zig покажутся знакомыми любому, кто писал на C, но в каждой есть аккуратные улучшения. Главное правило: никакого скрытого поведения. switch не проваливается между ветками, if можно использовать как выражение, а циклы интегрированы с опциональными типами и ошибками.

if как выражение

const n: i32 = 7;
const sign = if (n > 0) "плюс" else if (n < 0) "минус" else "ноль";
// sign == "плюс"

Здесь if возвращает значение, как тернарный оператор в C, но без отдельного синтаксиса ? :. Условие обязано быть типа bool — целое число «как истинность» не сработает, в отличие от C.

Цикл while

var i: u32 = 0;
var sum: u32 = 0;
while (i < 5) : (i += 1) {
    sum += i;
}
// sum == 0+1+2+3+4 == 10

Часть после двоеточия — выражение продолжения: оно выполняется в конце каждой итерации. Это удобно, потому что отделяет «шаг» цикла от его тела, и continue всё равно его выполнит.

Цикл for по срезам

const items = [_]u8{ 10, 20, 30 };
for (items, 0..) |value, index| {
    std.debug.print("{d}: {d}\n", .{ index, value });
}

Цикл for в Zig идёт по элементам массива или среза. Захват |value| даёт текущий элемент, а добавление 0.. и второго захвата |value, index| — ещё и индекс. Это безопаснее ручного индексирования: выход за границу невозможен.

switch без проваливания

const code: u8 = 2;
const name = switch (code) {
    0 => "ноль",
    1, 2, 3 => "мало",
    else => "много",
};
// name == "мало"

В отличие от C, ветки switch не проваливаются — не нужен break в каждой. Несколько значений объединяют запятой, диапазоны пишут как 1...9. switch обязан покрыть все случаи: либо перечислить все, либо добавить else.

Как работает под капотом

Блок с меткой возвращает значение через break :label value. Компилятор разворачивает это в обычное вычисление без накладных расходов. Поскольку switch обязан быть исчерпывающим, при добавлении нового варианта перечисления компилятор заставит вас обработать его — это ловит забытые случаи на этапе сборки.

Частые ошибки

Программисты из C пишут if (x) для ненулевого числа — Zig требует именно bool, так что нужно if (x != 0). Вторая ловушка — ожидать проваливания в switch и забыть, что в Zig его нет. Третья — неисчерпывающий switch без else: компилятор не пропустит.

Итог

  • if — выражение; условие обязано быть bool, а не числом.
  • У while есть выражение продолжения после двоеточия.
  • for идёт по элементам среза, индекс добавляют через 0...
  • switch не проваливается и обязан покрыть все случаи.
Проверьте себя
1. Можно ли в Zig написать if (x) для целого числа x, чтобы проверить «не ноль»?
AДа, как в C
BНет, условие должно быть bool — нужно if (x != 0)
CДа, но только для беззнаковых
DТолько в release-сборке
2. Нужен ли break в каждой ветке switch в Zig?
AДа, как в C
BНет, ветки не проваливаются
CТолько в последней ветке
DТолько при числовых значениях