On branch main

modified:   src/ch09.md
new file:   src/ex-ch09-06.zig
This commit is contained in:
zed
2023-11-19 20:52:43 +03:00
parent dfcf1d5538
commit 2b2ecbc1c8
2 changed files with 126 additions and 6 deletions

View File

@@ -403,14 +403,14 @@ Zig нет свойств (это такие поля структуры/кла
символ `_` в качестве первого символа имен тех полей, которые
предназначены только для внутреннего использования.
Поскольку строки имеют тип `[]8` или `[]const u8`, список из байтов
(то есть `ArrayList(u8)`) это подходящий тип для построения
конструктора строк по типу `StringBuilder` в .NET или `strings.Builder` в Go.
Поскольку строки имеют тип `[]8` или `[]const u8`, список из байтов (то
есть `ArrayList(u8)`) это подходящий тип для построения конструктора
строк по типу `StringBuilder` в .NET или `strings.Builder` в Go.
Фактически, вы будете часто использовать в случаях, когда функция
приниммает `Writer` и вам на выходе нужна строка. Ранее мы видели пример,
в котором для вывода документа JSON на стандартный вывод
использовалась `std.json.stringify`. Вот пример использования
`ArrayList(u8)` для вывода в переменную:
в котором для вывода документа JSON на стандартный вывод использовалась
`std.json.stringify`. Вот пример использования `ArrayList(u8)` для вывода
в переменную:
```zig
const std = @import("std");
@@ -434,6 +434,92 @@ pub fn main() !void {
## `anytype`
Мы уже вскользь упоминали `anytype` в первой главе. Такой "тип" это
весьма полезеная форма утиной (неявной) типизации во время компиляции.
Вот простой логгер:
```zig
pub const Logger = struct {
level: Level,
// "error" is reserved, names inside an @"..." are always
// treated as identifiers
const Level = enum {
debug,
info,
@"error",
fatal,
};
fn info(logger: Logger, msg: []const u8, out: anytype) !void {
if (@intFromEnum(logger.level) <= @intFromEnum(Level.info)) {
try out.writeAll(msg);
}
}
};
```
Параметр `out` метода `info` имеет тип `anytype`.
Это означает, что `Logger` может использовать
любую структуру, у которой есть метод `writeAll`,
у которого на входе `[]const u8` и на выходе `!void`.
Проверка типов производится во время компиляции
(а не во время выполнения программы): для каждого
типа, использованного в качестве второго параметра `Logger.info`,
проверяется, есть ли у этого типа метод `writeAll`.
Если мы попытаемся вызвать `info` с типом, который
не имеет всех нужных функций (в нашем случае одной),
то мы получим ошибку компиляции:
```zig
var l = Logger{.level = .info};
try l.info("sever started", true);
```
Компилятор скажет, что у типа `bool` нет поля с именем `writeAll`.
Использование `writer` типа `ArrayList(u8)` (например) работает:
```zig
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
var l = Logger{.level = .info};
var arr = std.ArrayList(u8).init(allocator);
defer arr.deinit();
try l.info("sever started", arr.writer());
std.debug.print("{s}\n", .{arr.items});
}
```
Одним из существенных недостатков типа `anytype` является документация.
Давайте взглянем на сигнатуру функции `std.json.stringify`, которую мы
уже несколько раз использовали:
```zig
// I **hate** multi-line function definitions
// But I'll make an exception for a guide which
// you might be reading on a small screen.
fn stringify(
value: anytype,
options: StringifyOptions,
out_stream: anytype
) @TypeOf(out_stream).Error!void
```
Первый параметр, `value: anytype` вроде как очевиден: это что-то, что
нужно сериализовать и это что-то может быть чем угодно (на самом деле в
Zig существуют некоторые вещи, которые сериализатор JSON сериализовать не
сможет). А вот что касается третьего параметра, то, конечно, мы можем
догадываться, что это то, *куда* будет выведен документ, но для того,
чтобы понять, какие у этой сущности должны быть методы, нужно или глядеть
в исходный текст `stringify` или передать что-то первое под руку
попавшееся и использовать в качестве документации текст ошибок
компиляции.
## `@typeOf`
## Система сборки

34
src/ex-ch09-06.zig Normal file
View File

@@ -0,0 +1,34 @@
const std = @import("std");
pub const Logger = struct {
level: Level,
// "error" is reserved, names inside an @"..." are always
// treated as identifiers
const Level = enum {
debug,
info,
@"error",
fatal,
};
fn info(logger: Logger, msg: []const u8, out: anytype) !void {
if (@intFromEnum(logger.level) <= @intFromEnum(Level.info)) {
try out.writeAll(msg);
}
}
};
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
var l = Logger{.level = .info};
var arr = std.ArrayList(u8).init(allocator);
defer arr.deinit();
try l.info("sever started", arr.writer());
std.debug.print("{s}\n", .{arr.items});
}