On branch main
modified: src/ch02.md Untracked files: src/ch03.md
This commit is contained in:
82
src/ch02.md
82
src/ch02.md
@@ -3,7 +3,8 @@
|
|||||||
|
|
||||||
В этой главе мы рассмотрим то, что было опущено в предыдущей -
|
В этой главе мы рассмотрим то, что было опущено в предыдущей -
|
||||||
инструкции управления выполнением программы, а также типы данных,
|
инструкции управления выполнением программы, а также типы данных,
|
||||||
о которых мы ещё не упоминали (объединения и ошибки).
|
о которых мы ещё не упоминали - перечисления, объединения и ошибки,
|
||||||
|
как особый вид перечислений.
|
||||||
|
|
||||||
## Управление потоком выполнения
|
## Управление потоком выполнения
|
||||||
|
|
||||||
@@ -25,7 +26,7 @@
|
|||||||
слова `and` и `or`, а не "комбинации закорючек".
|
слова `and` и `or`, а не "комбинации закорючек".
|
||||||
|
|
||||||
Далее, оператор сравнения (`==`) не работает для срезов, например, для строк (`[]const u8`).
|
Далее, оператор сравнения (`==`) не работает для срезов, например, для строк (`[]const u8`).
|
||||||
В большинстве случаев для сравнения Вы будете использовать `std.mem.eql(u8, str1, str2)`,
|
В большинстве случаев для сравнения используется функция `std.mem.eql(u8, str1, str2)`,
|
||||||
которая сначала сравнивает длины срезов, а потом побайтно их содержимое.
|
которая сначала сравнивает длины срезов, а потом побайтно их содержимое.
|
||||||
|
|
||||||
Операторы `if`, `else if`, `else` в Zig ничем особо не примечательны:
|
Операторы `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-set>!<return-type>` это такой составной тип,
|
||||||
|
объединение с ошибкой (error union type) и это означает,
|
||||||
что наша `main` может вернуть *или* ошибку *или* что-то (ну, в данном случае ничто, `void`).
|
что наша `main` может вернуть *или* ошибку *или* что-то (ну, в данном случае ничто, `void`).
|
||||||
До этого момента мы были предельно явными: мы описали
|
До этого момента мы были предельно явными: мы описали
|
||||||
специальный набор возможных ошибок и использовали его в возвращаемом значении.
|
специальный набор возможных ошибок и использовали его в возвращаемом значении.
|
||||||
Однако, когда дело касается ошибок, у Zig имеется пара классных "трюков".
|
Однако, когда дело касается ошибок, у Zig имеется пара классных "трюков".
|
||||||
Во-первых, вместо того, чтобы писать `<error-set!<return-type>>`,
|
Во-первых, вместо того, чтобы писать `<error-set>!<return-type>`,
|
||||||
можно написать просто `!<return-type>` и тогда компилятор сам выведет
|
можно написать просто `!<return-type>` и тогда компилятор сам выведет
|
||||||
набор ошибок, который функция может в принципе вернуть. Так что можно писать так:
|
набор ошибок, который функция может в принципе вернуть. Так что можно писать так:
|
||||||
|
|
||||||
@@ -563,11 +565,11 @@ pub fn main() !void {
|
|||||||
На самом деле оба подхода (с явным указанием всего и без такового) не вполне
|
На самом деле оба подхода (с явным указанием всего и без такового) не вполне
|
||||||
эквивалентны. Например, ссылки на функции с неявным набором ошибок требуют
|
эквивалентны. Например, ссылки на функции с неявным набором ошибок требуют
|
||||||
специального типа, `anyerror`. Разработчики библиотек, возможно, предпочитают
|
специального типа, `anyerror`. Разработчики библиотек, возможно, предпочитают
|
||||||
быть более явными, так код становиится самодокументирующимся. Тк или иначе,
|
быть более явными, так код становиится самодокументирующимся. Так или иначе,
|
||||||
оба подхода имеют право на существаание и Вы можете использовать оба
|
оба подхода имеют право на существование и Вы можете использовать оба
|
||||||
по своему усмотрению.
|
по своему усмотрению.
|
||||||
|
|
||||||
Реальная ценность объединений-ошибок проявляется при обработке ошибок,
|
Реальная ценность объединений с ошибками проявляется при обработке ошибок,
|
||||||
для которой в Zig есть ключевые слова `catch` и `try` (но только это совсем
|
для которой в Zig есть ключевые слова `catch` и `try` (но только это совсем
|
||||||
не такие `try/catch/finally`, как в других языках программирования).
|
не такие `try/catch/finally`, как в других языках программирования).
|
||||||
После вызова функции, которая может вернуть ошибку, можно использовать
|
После вызова функции, которая может вернуть ошибку, можно использовать
|
||||||
@@ -620,19 +622,69 @@ try action(req, res);
|
|||||||
|
|
||||||
Это полезно в частности потому, что **ошибки должны быть обработаны**.
|
Это полезно в частности потому, что **ошибки должны быть обработаны**.
|
||||||
По большей части Вы будете использовать для этого `catch` или `try`,
|
По большей части Вы будете использовать для этого `catch` или `try`,
|
||||||
но объединения-ошибки также поддерживаются операторами `if` и `while`,
|
но объединения с ошибками также поддерживаются операторами `if` и `while`,
|
||||||
вполне аналогично тому, как они работают с опциональными значениями.
|
вполне аналогично тому, как они работают с опциональными значениями.
|
||||||
В случае `while`, если условие сразу же даёт ошибку, исполняется оборот `else`.
|
В случае `while`, если условие сразу же даёт ошибку, исполняется оборот `else`.
|
||||||
|
|
||||||
Имеется также специальный тип `anyerror`, в котором может содержаться
|
Имеется также специальный тип `anyerror`, в котором может содержаться
|
||||||
любая ошибка. Хоть мы и можем задать тип возвращаемого из функции
|
любая ошибка. Хоть мы и можем задать тип возвращаемого из функции
|
||||||
значения как в виде `anyerror!<ype>`, так и в виде `!<type>`, эти
|
значения как в виде `anyerror!<type>`, так и в виде `!<type>`, эти
|
||||||
две формы не являются эквивалентными. Выведенный набор ошибок создаётся
|
две формы не являются эквивалентными. Выведенный набор ошибок создаётся
|
||||||
на основе конкретных ошибок, возвращаемых функций, в то время как
|
на основе конкретных ошибок, возвращаемых функций, в то время как
|
||||||
`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 имеются некоторые особенности, которые предоставляют нам
|
||||||
|
ещё более широкие возможности, то, что мы видели в первых двух главах,
|
||||||
|
уже составляет значительную часть языка. И это будет служить нам базой
|
||||||
|
при исследовании более сложных тем без отвлечения на синтаксис.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user