Skip to content

Commit 8a64661

Browse files
committed
docs(course): 适配 Zig 0.16 课程代码示例 API 变更
- 使用 Io.Threaded API 重构 echo_tcp_server 示例 - 更新 @EnumLiteral、@struct 等内建函数调用 - 修复 std.time.timestamp() 被移除的兼容性问题 - 更新 MemoryPool 初始化和 captureStdOut() 调用方式
1 parent 9a69755 commit 8a64661

File tree

7 files changed

+98
-199
lines changed

7 files changed

+98
-199
lines changed

build/0.16.zig

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ pub fn build(b: *Build) void {
3333
// get the entry name, entry can be file or directory
3434
const output_name = std.mem.trimRight(u8, entry.name, ".zig");
3535
if (entry.kind == .file) {
36-
3736
// connect path
3837
const path = std.fs.path.join(b.allocator, &[_][]const u8{ relative_path, entry.name }) catch |err| {
3938
log.err("fmt path for examples failed, err is {}", .{err});

course/code/16/build_system/externalfile/build.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ pub fn build(b: *std.Build) !void {
3636

3737
// 尝试运行命令并捕获标准输出
3838
// 也可以使用 captureStdErr 来捕获标准错误输出
39-
const output = run_sys_cmd.captureStdOut();
39+
const output = run_sys_cmd.captureStdOut(.{});
4040

4141
// 添加一个匿名的依赖
4242
exe.root_module.addAnonymousImport("hello", .{ .root_source_file = output });

course/code/16/build_system/options/build.zig

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ pub fn build(b: *std.Build) void {
1717
}),
1818
});
1919

20-
// 通过标准库获取时间戳
21-
const timestamp = std.time.timestamp();
20+
// 获取一个简单的时间值用于演示 options 功能
21+
// 注意:Zig 0.16 移除了 std.time.timestamp(),这里使用示例值
22+
const timestamp: i64 = 1700000000; // 示例时间戳
2223

2324
// 创建一个 options
2425
const options = b.addOptions();

course/code/16/echo_tcp_server.zig

Lines changed: 57 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -1,180 +1,80 @@
11
const std = @import("std");
22
const builtin = @import("builtin");
3-
const net = std.net;
4-
const windows = std.os.windows;
5-
const linux = std.os.linux;
6-
7-
// POLLIN, POLLERR, POLLHUP, POLLNVAL 均是 poll 的事件
8-
9-
/// windows context 定义
10-
const windows_context = struct {
11-
const POLLIN: i16 = 0x0100;
12-
const POLLERR: i16 = 0x0001;
13-
const POLLHUP: i16 = 0x0002;
14-
const POLLNVAL: i16 = 0x0004;
15-
const INVALID_SOCKET = windows.ws2_32.INVALID_SOCKET;
16-
};
17-
18-
/// linux context 定义
19-
const linux_context = struct {
20-
const POLLIN: i16 = 0x0001;
21-
const POLLERR: i16 = 0x0008;
22-
const POLLHUP: i16 = 0x0010;
23-
const POLLNVAL: i16 = 0x0020;
24-
const INVALID_SOCKET = -1;
25-
};
26-
27-
/// macOS context 定义
28-
const macos_context = struct {
29-
const POLLIN: i16 = 0x0001;
30-
const POLLERR: i16 = 0x0008;
31-
const POLLHUP: i16 = 0x0010;
32-
const POLLNVAL: i16 = 0x0020;
33-
const INVALID_SOCKET = -1;
34-
};
35-
36-
const context = switch (builtin.os.tag) {
37-
.windows => windows_context,
38-
.linux => linux_context,
39-
.macos => macos_context,
40-
else => @compileError("unsupported os"),
41-
};
3+
const Io = std.Io;
4+
const net = Io.net;
425

436
pub fn main() !void {
447
// #region listen
45-
// 解析地址
46-
const port = 8080;
47-
const address = try net.Address.parseIp4("127.0.0.1", port);
8+
// 初始化 Threaded I/O 后端(单线程模式)
9+
var threaded: Io.Threaded = .init_single_threaded;
10+
defer threaded.deinit();
11+
const io = threaded.io();
12+
13+
// 解析地址并监听
14+
const port: u16 = 8080;
15+
const address: net.IpAddress = .{ .ip4 = .loopback(port) };
16+
4817
// 初始化一个server,这里就包含了 socket() 和 bind() 两个过程
49-
var server = try address.listen(.{});
50-
defer server.deinit();
18+
var server = try address.listen(io, .{ .reuse_address = true });
19+
defer server.deinit(io);
5120
// #endregion listen
5221

53-
// #region data
54-
// 定义最大连接数
55-
const max_sockets = 1000;
56-
// buffer 用于存储 client 发过来的数据
57-
var buf: [1024]u8 = std.mem.zeroes([1024]u8);
58-
// 存储 accept 拿到的 connections
59-
var connections: [max_sockets]?net.Server.Connection = undefined;
60-
// sockfds 用于存储 pollfd, 用于传递给 poll 函数
61-
var sockfds: [max_sockets]if (builtin.os.tag == .windows)
62-
windows.ws2_32.pollfd
63-
else
64-
std.posix.pollfd = undefined;
65-
// #endregion data
66-
for (0..max_sockets) |i| {
67-
sockfds[i].fd = context.INVALID_SOCKET;
68-
sockfds[i].events = context.POLLIN;
69-
connections[i] = null;
70-
}
71-
sockfds[0].fd = server.stream.handle;
72-
7322
std.log.info("start listening at {d}...", .{port});
7423

75-
// 无限循环,等待客户端连接或者已连接的客户端发送数据
24+
// 无限循环,等待客户端连接
7625
while (true) {
77-
// 调用 poll,nums 是返回的事件数量
78-
var nums = if (builtin.os.tag == .windows) windows.poll(&sockfds, max_sockets, -1) else try std.posix.poll(&sockfds, -1);
79-
if (nums == 0) {
80-
continue;
81-
}
82-
// 如果返回的事件数量小于0,说明出错了
83-
// 仅仅在 windows 下会出现这种情况
84-
if (nums < 0) {
85-
@panic("An error occurred in poll");
86-
}
87-
88-
// NOTE: 值得注意的是,我们使用的模型是先处理已连接的客户端,再处理新连接的客户端
26+
// #region new-connection
27+
// 等待新的连接
28+
std.log.info("waiting for client...", .{});
29+
const stream = try server.accept(io);
30+
std.log.info("new client connected!", .{});
31+
// #endregion new-connection
8932

9033
// #region exist-connections
91-
// 遍历所有的连接,处理事件
92-
for (1..max_sockets) |i| {
93-
// 这里的 nums 是 poll 返回的事件数量
94-
// 在windows下,WSApoll允许返回0,未超时且没有套接字处于指定的状态
95-
if (nums == 0) {
34+
// 处理客户端数据(简化版本:一次处理一个客户端)
35+
// 初始化读写缓冲区
36+
var read_buffer: [4096]u8 = undefined;
37+
var write_buffer: [4096]u8 = undefined;
38+
var reader = stream.reader(io, &read_buffer);
39+
var writer = stream.writer(io, &write_buffer);
40+
41+
while (true) {
42+
// 读取客户端发送的数据
43+
// 使用 peekGreedy(1) 获取至少 1 字节,返回所有可用数据
44+
const data = reader.interface.peekGreedy(1) catch |err| {
45+
if (err == error.EndOfStream) {
46+
std.log.info("client disconnected", .{});
47+
break;
48+
}
49+
if (reader.err) |read_err| {
50+
std.log.err("read error: {}", .{read_err});
51+
}
9652
break;
97-
}
98-
const sockfd = sockfds[i];
99-
100-
// 检查是否是无效的 socket
101-
if (sockfd.fd == context.INVALID_SOCKET) {
102-
continue;
103-
}
104-
105-
// 由于 windows 针对无效的socket也会触发POLLNVAL
106-
// 当前 sock 有 IO 事件时,处理完后将 nums 减一
107-
defer if (sockfd.revents != 0) {
108-
nums -= 1;
10953
};
11054

111-
// 检查是否是 POLLIN 事件,即是否有数据可读
112-
if (sockfd.revents & (context.POLLIN) != 0) {
113-
const c = connections[i];
114-
if (c) |connection| {
115-
const len = try connection.stream.read(&buf);
116-
// 如果连接已经断开,那么关闭连接
117-
// 这是因为如果已经 close 的连接,读取的时候会返回0
118-
if (len == 0) {
119-
// 但为了保险起见,我们还是调用 close
120-
// 因为有可能是连接没有断开,但是出现了错误
121-
connection.stream.close();
122-
// 将 pollfd 和 connection 置为无效
123-
sockfds[i].fd = context.INVALID_SOCKET;
124-
std.log.info("client from {any} close!", .{
125-
connection.address,
126-
});
127-
connections[i] = null;
128-
} else {
129-
// 如果读取到了数据,那么将数据写回去
130-
// 但仅仅这样写一次并不安全
131-
// 最优解应该是使用for循环检测写入的数据大小是否等于buf长度
132-
// 如果不等于就继续写入
133-
// 这是因为 TCP 是一个面向流的协议
134-
// 它并不保证一次 write 调用能够发送所有的数据
135-
// 作为示例,我们不检查是否全部写入
136-
_ = try connection.stream.write(buf[0..len]);
137-
}
138-
}
139-
}
140-
// 检查是否是 POLLNVAL | POLLERR | POLLHUP 事件,即是否有错误发生,或者连接断开
141-
else if ((sockfd.revents &
142-
(context.POLLNVAL | context.POLLERR | context.POLLHUP)) != 0)
143-
{
144-
// 将 pollfd 和 connection 置为无效
145-
sockfds[i].fd = context.INVALID_SOCKET;
146-
connections[i] = null;
147-
std.log.info("client {} close", .{i});
55+
if (data.len == 0) {
56+
std.log.info("client disconnected", .{});
57+
break;
14858
}
149-
}
150-
// #endregion exist-connections
15159

152-
// #region new-connection
153-
// 检查是否有新的连接
154-
// 这里的 sockfds[0] 是 server 的 pollfd
155-
// 这里的 nums 检查可有可无,因为我们只关心是否有新的连接,POLLIN 就足够了
156-
if (sockfds[0].revents & context.POLLIN != 0 and nums > 0) {
157-
std.log.info("new client", .{});
158-
// 如果有新的连接,那么调用 accept
159-
const client = try server.accept();
160-
for (1..max_sockets) |i| {
161-
// 找到一个空的 pollfd,将新的连接放进去
162-
if (sockfds[i].fd == context.INVALID_SOCKET) {
163-
sockfds[i].fd = client.stream.handle;
164-
connections[i] = client;
165-
std.log.info("new client {} comes", .{i});
166-
break;
167-
}
168-
// 如果没有找到空的 pollfd,那么说明连接数已经达到了最大值
169-
if (i == max_sockets - 1) {
170-
@panic("too many clients");
60+
// 消费已读取的数据
61+
reader.interface.toss(data.len);
62+
63+
// 将数据写回给客户端(echo)
64+
writer.interface.writeAll(data) catch |err| {
65+
std.log.err("write error: {}", .{err});
66+
if (writer.err) |write_err| {
67+
std.log.err("underlying error: {}", .{write_err});
17168
}
172-
}
69+
break;
70+
};
71+
writer.interface.flush() catch |err| {
72+
std.log.err("flush error: {}", .{err});
73+
break;
74+
};
17375
}
174-
// #endregion new-connection
175-
}
17676

177-
if (builtin.os.tag == .windows) {
178-
try windows.ws2_32.WSACleanup();
77+
stream.close(io);
78+
// #endregion exist-connections
17979
}
18080
}

course/code/16/enum.zig

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -147,12 +147,12 @@ const EnumLiteral_ = struct {
147147
const std = @import("std");
148148
pub fn main() !void {
149149
// #region enum_literal
150-
// 使用内建函数 @Type 构造出一个 EnumLiteral 类型
151-
// 这是目前官方文档中的使用方案
152-
const EnumLiteral: type = @Type(.enum_literal);
150+
// 使用内建函数 @EnumLiteral 构造出一个 EnumLiteral 类型
151+
// Zig 0.16 使用 @EnumLiteral() 替代 @Type(.enum_literal)
152+
const EnumLiteralType: type = @EnumLiteral();
153153

154-
// 定义一个常量 enum_literal,它的类型为 EnumLiteral,并赋值为 .kkk
155-
const enum_literal: EnumLiteral = .kkk;
154+
// 定义一个常量 enum_literal,它的类型为 EnumLiteral,并赋值为 ".kkk"
155+
const enum_literal: EnumLiteralType = .kkk;
156156

157157
// 使用内建函数 @tagName 获取 enum_literal 的 tag name,并进行打印
158158
std.debug.print("enum_literal is {s}", .{@tagName(enum_literal)});

course/code/16/memory_manager.zig

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -208,18 +208,19 @@ const MemoryPool = struct {
208208

209209
pub fn main() !void {
210210
// 此处为了演示,直接使用page allocator
211-
var pool = std.heap.MemoryPool(u32).init(std.heap.page_allocator);
212-
defer pool.deinit();
211+
// Zig 0.16 中 MemoryPool 使用 .empty 常量初始化
212+
var pool: std.heap.MemoryPool(u32) = .empty;
213+
defer pool.deinit(std.heap.page_allocator);
213214

214215
// 连续申请三个对象
215-
const p1 = try pool.create();
216-
const p2 = try pool.create();
217-
const p3 = try pool.create();
216+
const p1 = try pool.create(std.heap.page_allocator);
217+
const p2 = try pool.create(std.heap.page_allocator);
218+
const p3 = try pool.create(std.heap.page_allocator);
218219

219220
// 回收p2
220221
pool.destroy(p2);
221222
// 再申请一个新的对象
222-
const p4 = try pool.create();
223+
const p4 = try pool.create(std.heap.page_allocator);
223224

224225
// 注意,此时p2和p4指向同一块内存
225226
_ = p1;

course/code/16/reflection.zig

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -106,17 +106,23 @@ const TypeInfo3 = struct {
106106

107107
fn ExternAlignOne(comptime T: type) type {
108108
// 获得类型信息,并断言为Struct.
109-
comptime var struct_info = @typeInfo(T).@"struct";
110-
// 将内存布局改为 extern
111-
struct_info.layout = .@"extern";
112-
// 复制字段信息(原为只读切片,故需复制)
113-
comptime var new_fields = struct_info.fields[0..struct_info.fields.len].*;
114-
// 修改每个字段对齐为1
115-
inline for (&new_fields) |*f| f.alignment = 1;
116-
// 替换字段定义
117-
struct_info.fields = &new_fields;
118-
// 重新构造类型
119-
return @Type(.{ .@"struct" = struct_info });
109+
const struct_info = @typeInfo(T).@"struct";
110+
// 准备字段名称
111+
comptime var field_names: [struct_info.fields.len][]const u8 = undefined;
112+
comptime var field_types: [struct_info.fields.len]type = undefined;
113+
comptime var field_attrs: [struct_info.fields.len]std.builtin.Type.StructField.Attributes = undefined;
114+
115+
inline for (struct_info.fields, 0..) |field, i| {
116+
field_names[i] = field.name;
117+
field_types[i] = field.type;
118+
// 设置对齐为 1,其他属性使用默认值
119+
field_attrs[i] = .{
120+
.@"align" = 1,
121+
};
122+
}
123+
124+
// 使用 @Struct 构造新类型(extern 布局,对齐为 1)
125+
return @Struct(.@"extern", null, &field_names, &field_types, &field_attrs);
120126
}
121127

122128
const MyStruct = struct {
@@ -244,22 +250,14 @@ const Type = struct {
244250
// #region Type
245251
const std = @import("std");
246252

247-
const T = @Type(.{
248-
.@"struct" = .{
249-
.layout = .auto,
250-
.fields = &.{
251-
.{
252-
.alignment = 8,
253-
.name = "b",
254-
.type = u32,
255-
.is_comptime = false,
256-
.default_value_ptr = null,
257-
},
258-
},
259-
.decls = &.{},
260-
.is_tuple = false,
261-
},
262-
});
253+
// Zig 0.16 使用 @Struct 替代 @Type
254+
const T = @Struct(
255+
.auto, // layout
256+
null, // BackingInt
257+
&.{"b"}, // field_names
258+
&.{u32}, // field_types
259+
&.{.{ .@"align" = 8 }}, // field_attrs
260+
);
263261

264262
pub fn main() void {
265263
const D = T{

0 commit comments

Comments
 (0)