# Глава III. Руководство по стилю оформления

В этой короткой главе мы рассмотрим два правила оформления, которые обеспечиваются компилятором, а также различные соглашения об именовании в стандартной библиотеке Zig.

# Неиспользуемые переменные

Zig не позволяет оставлять переменные неиспользованными. При компиляции следующего кода мы получим две ошибки:

const std = @import("std");

pub fn main() void {
    const sum = add(8999, 2);
}

fn add(a: i64, b: i64) i64 {
    // обратите внимание, что тут a + a, а не a + b
    return a + a;
}
src/ex-ch03-01.zig:5:11: error: unused local constant
    const sum = add(8999, 2);
          ^~~
src/ex-ch03-01.zig:8:16: error: unused function parameter
fn add(a: i64, b: i64) i64 {

Первая ошибка возникла потому, что локальная константа sum никак после присваивания ей значения не используется. Вторая ошибка связана с тем обстоятельством, что b (второй аргумент функции add) тоже внутри функции никак не используется. Однако, у вас могут быть вполне уважительные причины реально иметь в своём коде неиспользованные локальные переменные или параметры функций, поэтому в Zig имеется возможность игнорировать ненужные значения путём присваивания их символу _, вот таким образом:

const std = @import("std");

pub fn main() void {
    _ = add(8999, 2);

    // или

    sum = add(8999, 2);
    _ = sum;
}

fn add(a: i64, b: i64) i64 {
    _ = b;
    return a + a;
}

Вместо того, чтобы внутри тела add написать _ = b;, мы могли бы использовать _ в её заголовке, то есть fn add(a: i64, _: i64) i64 {, однако, такой вариант менее желателен, поскольку заставляет пользователя гадать, что собой представляет параметр b.

Обратите также внимание, что std в этом примере никак не используется, однако, это не порождает ошибку. Возможно, в будущем Zig будет считать это ошибкой.

# Маскирование/затенение имён (shadowing)

Zig не позволяет одному идентификатору "перекрывать" собой другой, используя то же самое имя. Следующий код для чтения из сокета не является корректным:

fn read(stream: std.net.Stream) ![]const u8 {
    var buf: [512]u8 = undefined;
    const read = try stream.read(&buf);
    if (read == 0) {
        return error.Closed;
    }
    return buf[0..read];
}

Тут переменная read конфликтует с именем самой функции.

# Соглашения об именовании

За исключением правил, диктуемых компилятором, вы, разумеется, вольны использовать такие соглашения об именах, какие вам больше нравятся. Однако, совершенно не вредно понимать собственные соглашения Zig, поскольку стандартная библиотека, которой вы, безусловно, будете пользоваться, придерживается этих соглашений.

Отступы в Zig - это 4 пробела. Как правило, текстовые редакторы можно настроить так, чтобы клавиша Tab вставляла именно 4 пробела, а не символ табуляции и это гораздо удобней, чем 4 раза нажимать пробел.

Имена функций пишутся в camelCase, имена переменных - в snake_case. Типы пишутся в PascalCase. Есть одно интересное пересечение этих 3-х правил. Переменные, которые хранят тип или функции, которые возвращают тип, так же, как и сами типы, пишутся в PascalCase. Мы уже видели нечто подобное:

// смотрим на встроенную функцию '@TypeOf'
std.debug.print("{any}\n", .{@TypeOf(.{.year = 2023, .month = 8})});

Мы уже видели некоторые другие встроенные функции, как то @import, @rem и @intCast. Поскольку это функции, их имена записаны в camelCase. Однако, @TypeOf это тоже (встроенная) функция, но она почему-то написана в PascalCase. А это потому, что она возвращает тип и поэтому для её имени используется соглашение для типов, а не для функций. Если бы мы присваивали результат функции @TypeOf какой-то переменной, эту переменную так же следовало бы писать в CamelCase:

const T = @TypeOf(3)
std.debug.print("{any}\n", .{T});

У компилятора Zig есть команда fmt, которая форматирует обозначенные файлы по своим правилам. Впрочем, такое форматирование затрагивает не всё: например, оно подправит отступы и положение фигурных скобок для блоков, но регистр идентификаторов останется как есть.