#
Глава 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
, которая форматирует обозначенные
файлы по своим правилам. Впрочем, такое форматирование затрагивает не всё:
например, оно подправит отступы и положение фигурных скобок для блоков,
но регистр идентификаторов останется как есть.