diff --git a/src/ch02.md b/src/ch02.md index 783479d..166487f 100644 --- a/src/ch02.md +++ b/src/ch02.md @@ -3,7 +3,8 @@ В этой главе мы рассмотрим то, что было опущено в предыдущей - инструкции управления выполнением программы, а также типы данных, -о которых мы ещё не упоминали (объединения и ошибки). +о которых мы ещё не упоминали - перечисления, объединения и ошибки, +как особый вид перечислений. ## Управление потоком выполнения @@ -25,7 +26,7 @@ слова `and` и `or`, а не "комбинации закорючек". Далее, оператор сравнения (`==`) не работает для срезов, например, для строк (`[]const u8`). -В большинстве случаев для сравнения Вы будете использовать `std.mem.eql(u8, str1, str2)`, +В большинстве случаев для сравнения используется функция `std.mem.eql(u8, str1, str2)`, которая сначала сравнивает длины срезов, а потом побайтно их содержимое. Операторы `if`, `else if`, `else` в Zig ничем особо не примечательны: @@ -251,7 +252,7 @@ const personality_analysis = blk: { Блоки подобного рода нужно оканчивать точкой с запятой. -Позже, когда мы будем изучать объединения с пометками, +Позже, когда мы будем изучать маркированные объединения, объединения-ошибки и типы с необязательным значением, мы узнаем, какие ещё возможности предоставляют управляющие структуры. @@ -269,7 +270,7 @@ const Status = enum { }; ``` -И. как и структуры, перечисления могут содержать другие определения, +Так же, как и структуры, перечисления могут содержать другие определения, включая функции, которые в качестве аргумента могут использовать перечисление, в которое они входят (а могут и не использовать): @@ -539,12 +540,13 @@ pub fn main() OpenError!void { } ``` -Это называется объединение-ошибка (error unon type) и означает, +Комбинация `!` это такой составной тип, +объединение с ошибкой (error union type) и это означает, что наша `main` может вернуть *или* ошибку *или* что-то (ну, в данном случае ничто, `void`). До этого момента мы были предельно явными: мы описали специальный набор возможных ошибок и использовали его в возвращаемом значении. Однако, когда дело касается ошибок, у Zig имеется пара классных "трюков". -Во-первых, вместо того, чтобы писать `>`, +Во-первых, вместо того, чтобы писать `!`, можно написать просто `!` и тогда компилятор сам выведет набор ошибок, который функция может в принципе вернуть. Так что можно писать так: @@ -563,11 +565,11 @@ pub fn main() !void { На самом деле оба подхода (с явным указанием всего и без такового) не вполне эквивалентны. Например, ссылки на функции с неявным набором ошибок требуют специального типа, `anyerror`. Разработчики библиотек, возможно, предпочитают -быть более явными, так код становиится самодокументирующимся. Тк или иначе, -оба подхода имеют право на существаание и Вы можете использовать оба +быть более явными, так код становиится самодокументирующимся. Так или иначе, +оба подхода имеют право на существование и Вы можете использовать оба по своему усмотрению. -Реальная ценность объединений-ошибок проявляется при обработке ошибок, +Реальная ценность объединений с ошибками проявляется при обработке ошибок, для которой в Zig есть ключевые слова `catch` и `try` (но только это совсем не такие `try/catch/finally`, как в других языках программирования). После вызова функции, которая может вернуть ошибку, можно использовать @@ -620,19 +622,69 @@ try action(req, res); Это полезно в частности потому, что **ошибки должны быть обработаны**. По большей части Вы будете использовать для этого `catch` или `try`, -но объединения-ошибки также поддерживаются операторами `if` и `while`, +но объединения с ошибками также поддерживаются операторами `if` и `while`, вполне аналогично тому, как они работают с опциональными значениями. В случае `while`, если условие сразу же даёт ошибку, исполняется оборот `else`. Имеется также специальный тип `anyerror`, в котором может содержаться любая ошибка. Хоть мы и можем задать тип возвращаемого из функции -значения как в виде `anyerror!`, так и в виде `!`, эти +значения как в виде `anyerror!`, так и в виде `!`, эти две формы не являются эквивалентными. Выведенный набор ошибок создаётся на основе конкретных ошибок, возвращаемых функций, в то время как `anyerror` - это глобальный набор ошибок, множество всех ошибок, фигурирующих -в программе. - - - +в программе. Следовательно, использование `anyerror` в сигнатуре +функции это как бы обозначение того, что ваша функция номинально может вернуть +такие ошибки, какие в реальности она вернуть не может. +Тип `anyerror` используется для параметров функций или полей структур, +передаваемых в функцию, когда эта функция способна обрабатывать +любые возможные ошибки (ну, например, функция записи в журнал работы программы) + +Довольно часто используется комбинация объединения с ошибкой с опциональным типом. +Если набор ошибок выводится, то это записывается вот так: + +``` +/ load the last saved game +pub fn loadLast() !?Save { + // TODO + return null; +} +``` + +Существуют различные способы обрабатывать вызов таких функций, +но наиболее компактный это сначала использовать `try`, чтобы +извлечь ошибку и затем `orelse`, чтобы развернуть необязательное значение, +вот рабочий скелет: + +``` +const std = @import("std"); + +pub fn main() void { + // This is the line you want to focus on + const save = (try Save.loadLast()) orelse Save.blank(); + std.debug.print("{any}\n", .{save}); +} + +pub const Save = struct { + lives: u8, + level: u16, + + pub fn loadLast() !?Save { + //todo + return null; + } + + pub fn blank() Save { + return .{ + .lives = 3, + .level = 1, + }; + } +}; +``` + +Хотя в Zig имеются некоторые особенности, которые предоставляют нам +ещё более широкие возможности, то, что мы видели в первых двух главах, +уже составляет значительную часть языка. И это будет служить нам базой +при исследовании более сложных тем без отвлечения на синтаксис.