Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
300 changes: 145 additions & 155 deletions content/learn/coding-in-zig.smd
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
const std = @import("std");

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
var gpa = std.heap.DebugAllocator(.{}){};
const allocator = gpa.allocator();

var lookup = std.StringHashMap(User).init(allocator);
Expand Down Expand Up @@ -76,7 +76,7 @@ Goku's power is: -1431655766
const std = @import("std");

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
var gpa = std.heap.DebugAllocator(.{}){};
const allocator = gpa.allocator();

// User -> *const User
Expand Down Expand Up @@ -115,48 +115,52 @@ const User = struct {
const std = @import("std");
const builtin = @import("builtin");

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();

var lookup = std.StringHashMap(User).init(allocator);
defer lookup.deinit();

// stdin is an std.io.Reader
// the opposite of an std.io.Writer, which we already saw
const stdin = std.io.getStdIn().reader();

// stdout is an std.io.Writer
const stdout = std.io.getStdOut().writer();

var i: i32 = 0;
while (true) : (i += 1) {
var buf: [30]u8 = undefined;
try stdout.print("Please enter a name: ", .{});
if (try stdin.readUntilDelimiterOrEof(&buf, '\n')) |line| {
var name = line;
// Windows 平台换行以 `\r\n` 结束
// 所以需要截取\r以获取控制台输入字符
if (builtin.os.tag == .windows) {
name = @constCast(std.mem.trimRight(u8, name, "\r"));
}

if (name.len == 0) {
break;
}
try lookup.put(name, .{.power = i});
}
}

const has_leto = lookup.contains("Leto");
std.debug.print("{any}\n", .{has_leto});
pub fn main(init: std.process.Init) !void {

// 这里的`std.process.Init` 参数是我们之间从来没有见过的
// 他被称为“juicy main”,是在Zig 0.16版本中添加的,它是完全可选的。
// 但是它可以省去大多数程序都需要的一些设置样板代码。我在这里使用它来获取一个
// 默认的`Io`实例(顺便获取一个分配器)。
// `Io` 不在本教程的讨论范围内。
const io = init.io;
const allocator = init.gpa;

var lookup = std.StringHashMap(User).init(allocator);
defer lookup.deinit();

var buf: [30]u8 = undefined;
var stdin = std.Io.File.stdin().reader(io, &buf);

var stdout = std.Io.File.stdout().writer(io, &.{});

var i: i32 = 0;
while (true) : (i += 1) {
try stdout.interface.print("Please enter a name: ", .{});
var name = try stdin.interface.takeDelimiter('\n') orelse break;
if (builtin.os.tag == .windows) {
// In Windows lines are terminated by \r\n.
// We need to strip out the \r
name = @constCast(std.mem.trimEnd(u8, name, "\r"));
}
if (name.len == 0) {
break;
}
try lookup.put(name, .{.power = i});
}

const has_leto = lookup.contains("Leto");
std.debug.print("{any}\n", .{has_leto});
}

const User = struct {
power: i32,
power: i32,
};
```

> 这段代码的初始版本在Windows上无法编译成功。因此,需要添加你现在看到的`@constCast`。我们之前也见过其他的内置函数,但是这个函数更加高级一些。我曾考虑删除整行代码,但考虑到Windows用户也能轻松上周,所以还是保留了`@constCast`。虽然这种情况有更加简单的解决方案,但是我最终还是决定使用不安全的`@constCast`。我根据这个例子的情况写了[一篇博客文章](https://www.openmymind.net/Zigs-ConstCast/)来解释这样做的必要性--但是这篇文章要高级的多。建议你在深入学习zig后再来阅读这篇文章。



上述代码虽然区分大小写,但无论我们如何完美地输入 `Leto`,`contains` 总是返回 `false`。让我们通过遍历 `lookup` 打印其值来调试一下:

```zig
Expand Down Expand Up @@ -226,64 +230,60 @@ defer {

```zig
const std = @import("std");
const Allocator = std.mem.Allocator;
const builtin = @import("builtin");
const Allocator = std.mem.Allocator;

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();

var arr = std.ArrayList(User).init(allocator);
defer {
for (arr.items) |user| {
user.deinit(allocator);
}
arr.deinit();
}

// stdin is an std.io.Reader
// the opposite of an std.io.Writer, which we already saw
const stdin = std.io.getStdIn().reader();

// stdout is an std.io.Writer
const stdout = std.io.getStdOut().writer();

var i: i32 = 0;
while (true) : (i += 1) {
var buf: [30]u8 = undefined;
try stdout.print("Please enter a name: ", .{});
if (try stdin.readUntilDelimiterOrEof(&buf, '\n')) |line| {
var name = line;
if (builtin.os.tag == .windows) {
name = @constCast(std.mem.trimRight(u8, name, "\r"));
}

if (name.len == 0) {
break;
}
const owned_name = try allocator.dupe(u8, name);
try arr.append(.{.name = owned_name, .power = i});
}
}

var has_leto = false;
for (arr.items) |user| {
if (std.mem.eql(u8, "Leto", user.name)) {
has_leto = true;
break;
}
}

std.debug.print("{any}\n", .{has_leto});
pub fn main(init: std.process.Init) !void {
const io = init.io;
const allocator = init.gpa;

var arr: std.ArrayList(User) = .empty;
defer {
for (arr.items) |user| {
user.deinit(allocator);
}
arr.deinit(allocator);
}

var buf: [30]u8 = undefined;
var stdin = std.Io.File.stdin().reader(io, &buf);
var stdout = std.Io.File.stdout().writer(io, &.{});

var i: i32 = 0;
while (true) : (i += 1) {
try stdout.interface.print("Please enter a name: ", .{});
var name = try stdin.interface.takeDelimiter('\n') orelse break;

if (builtin.os.tag == .windows) {
// In Windows lines are terminated by \r\n.
// We need to strip out the \r
name = @constCast(std.mem.trimEnd(u8, name, "\r"));
}
if (name.len == 0) {
break;
}
const owned_name = try allocator.dupe(u8, name);
try arr.append(allocator, .{.name = owned_name, .power = i});
}

var has_leto = false;
for (arr.items) |user| {
if (std.mem.eql(u8, "Leto", user.name)) {
has_leto = true;
break;
}
}

std.debug.print("{any}\n", .{has_leto});
}

const User = struct {
name: []const u8,
power: i32,
name: []const u8,
power: i32,

fn deinit(self: User, allocator: Allocator) void {
allocator.free(self.name);
}
fn deinit(self: User, allocator: Allocator) void {
allocator.free(self.name);
}
};
```

Expand All @@ -297,8 +297,8 @@ const User = struct {
const std = @import("std");

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
var dpa = std.heap.DebugAllocator(.{}){};
const allocator = dpa.allocator();
Comment on lines +300 to +301

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In Zig, std.heap.DebugAllocator is a struct, not a generic function that takes a configuration parameter. Therefore, calling it with (.{}) is a compile error. It should be initialized directly as std.heap.DebugAllocator{}.

	var dpa = std.heap.DebugAllocator{};
	const allocator = dpa.allocator();


var out = std.ArrayList(u8).init(allocator);
defer out.deinit();
Expand Down Expand Up @@ -351,37 +351,31 @@ try l.info("sever started", true);
no field or member function named 'writeAll' in 'bool'
```

使用 `ArrayList(u8)` 的 `writer` 就可以运行:
但是如果我们传递一个实现了`writeAll`函数的`*std.Io.Writer`对象,代码就能正常运行

```zig
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
var l = Logger{.level = .info};

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});
const w = LineLimiter{.max = 80};
try l.info("a" ** 85, &w);
}
```

`anytype` 的一个最大缺点就是文档。下面是我们用过几次的 `std.json.stringify` 函数的签名:

```zig
// 我**讨厌**多行函数定义
// 不过,鉴于你可能在小屏幕上阅读这个指南,因此这里破一次例。

fn stringify(
value: anytype,
options: StringifyOptions,
out_stream: anytype
) @TypeOf(out_stream).Error!void
const LineLimiter = struct {
max: usize,

pub fn writeAll(self: *const LineLimiter, data: []const u8) !void {
var remaining = data;
while (remaining.len > 0) {
const len = @min(self.max, remaining.len);
std.debug.print("{s}\n", .{remaining[0..len]});
remaining = remaining[len..];
}
}
};
```

第一个参数 `value: anytype` 是显而易见的,它是要序列化的值,可以是任何类型(实际上,Zig 的 JSON 序列化器不能序列化某些类型,比如 HashMap)。我们可以猜测,`out_stream` 是写入 JSON 的地方,但至于它需要实现什么方法,你和我一样猜得到。唯一的办法就是阅读源代码,或者传递一个假值,然后使用编译器错误作为我们的文档。如果有更好的自动文档生成器,这一点可能会得到改善。不过,我希望 Zig 能提供接口,这已经不是第一次了
在这种情况下,LineLimiter最好实现`*std.Io.Writer`接口,一边更通用的使用。但是在Zig 0.15版本-该版本重新设计了Writer和Reader接口之前,使用anytype作为通用写入器是很常见的做法。虽然这种用法可能会被逐步淘汰,但你一定会看到并以大致相同的方式使用`anytype`;只是可能不太用于捕获不同的写入器

## [@TypeOf]($heading.id('typeof'))

Expand Down Expand Up @@ -473,16 +467,37 @@ run_step.dependOn(&run_cmd.step);
要添加『测试』步骤,你需要重复刚才添加的大部分运行代码,只是不再使用 `b.addExecutable`,而是使用 `b.addTest`:

```zig
const tests = b.addTest(.{
const mod = b.createModule(.{
.target = target,
.optimize = optimize,
.root_source_file = b.path("learning.zig"),
});

const test_cmd = b.addRunArtifact(tests);
test_cmd.step.dependOn(b.getInstallStep());
const test_step = b.step("test", "Run the tests");
test_step.dependOn(&test_cmd.step);
{
// setup exe
const exe = b.addExecutable(.{
.name = "learning",
.root_module = mod,
});
b.installArtifact(exe);

const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
const run_step = b.step("run", "Start learning!");
run_step.dependOn(&run_cmd.step);
}

{
// setup tests
const tests = b.addTest(.{
.root_module = mod,
});

const test_cmd = b.addRunArtifact(tests);
test_cmd.step.dependOn(b.getInstallStep());
const test_step = b.step("test", "Run the tests");
test_step.dependOn(&test_cmd.step);
}
```

我们将该步骤命名为 `test`。运行 `zig build --help` 会显示另一个可用步骤 `test`。由于我们没有进行任何测试,因此很难判断这一步是否有效。在 `learning.zig` 中,添加
Expand Down Expand Up @@ -574,45 +589,20 @@ pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});

const calc_module = b.addModule("calc", .{
const calc_module = b.createModule("calc", .{
.root_source_file = b.path("PATH_TO_CALC_PROJECT/calc.zig"),
});

{
// 设置我们的 "run" 命令。

const exe = b.addExecutable(.{
.name = "learning",
.target = target,
.optimize = optimize,
.root_source_file = b.path("learning.zig"),
});
// 添加这些代码
exe.root_module.addImport("calc", calc_module);
b.installArtifact(exe);

const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());

const run_step = b.step("run", "Start learning!");
run_step.dependOn(&run_cmd.step);
}
const mod = b.createModule(.{
.target = target,
.optimize = optimize,
.root_source_file = b.path("learning.zig"),
.imports = &.{
.{.name = "calc", .module = calc_module},
}
});

{
// 设置我们的 "test" 命令。
const tests = b.addTest(.{
.target = target,
.optimize = optimize,
.root_source_file = b.path("learning.zig"),
});
// 添加这行代码
tests.root_module.addImport("calc", calc_module);

const test_cmd = b.addRunArtifact(tests);
test_cmd.step.dependOn(b.getInstallStep());
const test_step = b.step("test", "Run the tests");
test_step.dependOn(&test_cmd.step);
}
// setup our exe and test like before
}
```

Expand Down
2 changes: 1 addition & 1 deletion content/learn/generics.smd
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ const std = @import("std");
const Allocator = std.mem.Allocator;
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
var gpa = std.heap.DebugAllocator(.{}){};
const allocator = gpa.allocator();
var list = try List(u32).init(allocator);
Expand Down
Loading