zig基础用法

遍历

1
2
3
4
5
6
7
8
const std = @import("std");

pub fn main() !void {
const data_list = [_]u8{ 'a', 'b', 'c' };
for (data_list, 0..data_list.len) |item, index| {
std.log.debug("index: {d}, item: {c}", .{ index, item });
}
}

错误处理

普通用法

向上抛出错误

1
2
3
4
5
6
const std = @import("std");

pub fn main() !void {
const file = try std.fs.cwd().openFile("does_not_exist/foo.txt", .{});
defer file.close();
}

捕获错误

1
2
3
4
5
6
7
8
9
10
11
12
const std = @import("std");

pub fn main() void {
const file = std.fs.cwd().openFile("does_not_exist/foo.txt", .{}) catch |err| {
std.log.debug("无法打开文件: {}", .{err});
return;
};
defer {
std.log.debug("错误的时候不会到这里", .{});
file.close();
}
}

自定义错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const std = @import("std");

const MyError = error{
AAA,
BBB,
CCC,
};

fn demo_func(num: i32) !i32 {
if (num < 0) {
return MyError.AAA;
} else {
return num * num;
}
}

pub fn main() void {
const result = demo_func(-111) catch |err| {
std.log.debug("custom error reason {}", .{err});
return;
};
std.log.debug("result {d}", .{result});
}

errdefer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const std = @import("std");

pub fn aa() !void {
errdefer {
std.log.debug("errdefer 清理垃圾", .{});
}
return error.aaa;
}

pub fn main() !void {
// try aa();

aa() catch |err| {
std.log.debug("捕获错误 {}", .{err});
};

std.log.debug("正常运行", .{});
}

泛型

1
2
3
4
5
6
7
8
9
10
11
12
const std = @import("std");

const MyType = u64;

fn max(comptime T: type, a: T, b: T) T {
return if (a > b) a else b;
}

pub fn main() void {
const result = max(MyType, 11, 22);
std.log.debug("result {d}", .{result});
}

回调函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const std = @import("std");

const CallBackProc = fn (arg: u64) void;

fn demo(comptime callback_proc: CallBackProc, ms: u64) void {
callback_proc(ms);
}

fn on_callback(argument: u64) void {
std.log.debug("proc args {d}", .{argument});
}

pub fn main() void {
demo(on_callback, 10000);
}

类型转换

1
2
3
4
5
6
7
8
9
10
11
const std = @import("std");

pub fn main() !void {
const a: u64 = 111;
const b: u32 = @truncate(a);
std.log.debug("a {d} b {d}", .{ a, b });

const isok = false;
const val: u8 = @intCast(@intFromBool(isok));
std.log.debug("isok {d}", .{val});
}

内存分配器

建议把 allocator 当作为参数,类型是 std.mem.Allocator

  • page_allocator

通常用于处理大块内存的分配, 很慢, 不需要 deinit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const std = @import("std");
const expect = std.testing.expect;

test "page allocator" {
const allocator = std.heap.page_allocator;

const memory = demo(allocator) catch |err| {
std.debug.print("error occurred: {}\n", .{err});
return;
};

try expect(memory.len == 100);
try expect(@TypeOf(memory) == []u8);
}

pub fn demo(allocator: std.mem.Allocator) ![]u8 {
const memory = try allocator.alloc(u8, 100);

defer {
std.debug.print("准备 free allocator\n", .{});
allocator.free(memory);
}

return memory;
}
  • fba

不能使用堆内存的时候用,比如写内核的时候, 如果字节用完,会报 OutOfMemory 错误

不需要 deinit, alloc 出来的内存可以多次 free

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const std = @import("std");
const expect = std.testing.expect;

test "fba" {
var buffer: [1000]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buffer);
const allocator = fba.allocator();

const memory = demo(allocator) catch |err| {
std.log.debug("error occurred: {}", .{err});
return;
};

try expect(memory.len == 100);
try expect(@TypeOf(memory) == []u8);
}

pub fn demo(allocator: std.mem.Allocator) ![]u8 {
const memory = try allocator.alloc(u8, 100);

defer {
std.debug.print("准备 free allocator\n", .{});
allocator.free(memory);
}

return memory;
}
  • gpa

为安全设计的内存分配器

deinit 返回状态值,用来检测内存泄漏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const std = @import("std");
const expect = std.testing.expect;

test "gpa" {
const config = .{};
var gpa = std.heap.GeneralPurposeAllocator(config){};
defer {
if (gpa.deinit() != .ok) {
@panic("发现内存泄漏");
}
}

const allocator = gpa.allocator();

const memory = demo(allocator) catch |err| {
std.log.debug("error occurred: {}", .{err});
return;
};

try expect(memory.len == 100);
try expect(@TypeOf(memory) == []u8);
}

pub fn demo(allocator: std.mem.Allocator) ![]u8 {
const memory = try allocator.alloc(u8, 100);

defer {
std.debug.print("准备 free allocator\n", .{});
allocator.free(memory);
}

return memory;
}
  • testing_allocator

用于测试用例里面检测 leak

运行 zig test xxx.zig 的时候才能用

1
const allocator = std.testing.allocator;
  • ArenaAllocator

用于短期内需要临时创建和销毁多个小对象

alloc 出来的内存会自动 free

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const std = @import("std");

test "arena" {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer {
std.debug.print("准备 arena Allocator.deinit\n", .{});
arena.deinit();
}

const allocator = arena.allocator();

const m1 = try allocator.alloc(u8, 1);
const m2 = try allocator.alloc(u8, 10);
_ = try allocator.alloc(u8, 100);

defer {
std.debug.print("准备 free arena Allocator\n", .{});
allocator.free(m1);
allocator.free(m2);
// allocator.free(m3);
}
}

封装

safe_gpa.zig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const std = @import("std");

pub const Gpa = struct {
gpa: std.heap.GeneralPurposeAllocator(.{}) = std.heap.GeneralPurposeAllocator(.{}){},

pub fn allocator(self: *Gpa) std.mem.Allocator {
return self.gpa.allocator();
}

pub fn deinit(self: *Gpa) void {
if (self.gpa.deinit() != .ok) {
@panic("发现内存泄漏");
}
}
};

pub fn init() Gpa {
return Gpa{};
}

main.zig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const std = @import("std");
const safe_gpa = @import("safe_gpa.zig");

pub fn main() !void {
var gpa = safe_gpa.init();
defer gpa.deinit();

const gpa_allocator = gpa.allocator();

// 外面包一层线程安全的 allocator
var thread_safe_fba = std.heap.ThreadSafeAllocator{ .child_allocator = gpa_allocator };
const thread_safe_allocator = thread_safe_fba.allocator();

// 外面包一层 arena 分配器
var arena = std.heap.ArenaAllocator.init(thread_safe_allocator);
defer arena.deinit();
const allocator = arena.allocator();

const formatted_str = try std.fmt.allocPrint(allocator, "测试 {}", .{42});
// defer allocator.free(formatted_str);

std.log.debug("{s}", .{formatted_str});
}

指针

1
2
3
4
5
6
7
8
const std = @import("std");

pub fn main() !void {
var value: i32 = 42;
const ptr: *const i32 = &value;

std.log.debug("value: {}", .{ptr.*});
}

array_list

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const std = @import("std");
const equal = std.mem.eql;
const ArrayList = std.ArrayList;
const allocator = std.heap.page_allocator;

pub fn main() !void {
var array_list = ArrayList(u8).init(allocator);
defer array_list.deinit();

try array_list.append('H');
try array_list.append('e');
try array_list.append('l');
try array_list.append('l');
try array_list.append('o');
try array_list.appendSlice(" World!");

std.log.debug("{s}", .{array_list.items});
}

array

array 固定大小

slice 动态大小

1
2
3
4
5
6
7
8
9
10
const std = @import("std");

pub fn abc(list: []u8) void {
std.log.debug("in abc {any}", .{list});
}

pub fn main() !void {
var list = [_]u8{ 11, 22, 33, 44 };
abc(list[0..]);
}

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const std = @import("std");

const allocator = std.heap.page_allocator;

pub fn demo(list: []u8, data_list: []u8) []u8 {
for (list, 0..list.len) |_, index| {
data_list[index] = @truncate(index);
}

return data_list[0..];
}

pub fn main() void {
var list = [_]u8{ 11, 22, 33, 44 };
const data_list = allocator.alloc(u8, list.len) catch |err| {
std.log.debug("err {any}", .{err});
return;
};

defer allocator.free(data_list);
const aa = demo(list[0..], data_list);
std.log.debug("result {any}", .{aa});
}

enum

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const std = @import("std");
const expect = std.testing.expect;

const Result = enum(u32) {
aa,
bb = 1000,
cc = 1000000,
dd,
};

test "set enum ordinal value" {
try expect(@intFromEnum(Result.aa) == 0);
try expect(@intFromEnum(Result.bb) == 1000);
try expect(@intFromEnum(Result.cc) == 1000000);
try expect(@intFromEnum(Result.dd) == 1000001);
}

hash_map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
const std = @import("std");
const expect = std.testing.expect;

const allocator = std.testing.allocator;

test "hash_map" {
const Point = struct { x: i32, y: i32 };

var hash_map = std.AutoHashMap(u32, Point).init(
allocator,
);
defer hash_map.deinit();

try hash_map.put(1525, .{ .x = 1, .y = -4 });
try hash_map.put(1550, .{ .x = 2, .y = -3 });
try hash_map.put(1575, .{ .x = 3, .y = -2 });
try hash_map.put(1600, .{ .x = 4, .y = -1 });

try expect(hash_map.count() == 4);

var sum = Point{ .x = 0, .y = 0 };
var iterator = hash_map.iterator();

while (iterator.next()) |item| {
std.log.debug("key {d}, x {d}, y {d}", .{ item.key_ptr.*, item.value_ptr.x, item.value_ptr.y });
sum.x += item.value_ptr.x;
sum.y += item.value_ptr.y;
}

try expect(sum.x == 10);
try expect(sum.y == -10);
}

json

标准库里面, 需要先定义 struct, 比较挫

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
const std = @import("std");

const Place = struct { lat: f32, long: f32 };

fn str_to_json(allocator: std.mem.Allocator, json_str: []u8) !Place {
const parsed = try std.json.parseFromSlice(
Place,
allocator,
json_str,
.{},
);
defer parsed.deinit();

const place = parsed.value;
return place;
}

fn json_to_str(allocator: std.mem.Allocator, json_obj: Place) ![]u8 {
var json_str = std.ArrayList(u8).init(allocator);
try std.json.stringify(json_obj, .{}, json_str.writer());

std.log.debug("{s}", .{json_str.items});

return json_str.items;
}

pub fn main() !void {
const allocator = std.heap.page_allocator;

const obj = Place{
.lat = 51.997664,
.long = -0.740687,
};
const json_str = try json_to_str(allocator, obj);
const json_obj = try str_to_json(allocator, json_str);
std.log.debug("{any}", .{json_obj.long});
}

log

1
2
3
4
5
6
7
const std = @import("std");

pub fn main() void {
const info = "hi";
const data_list = [_]u8{ 11, 22, 33, 44, 55 };
std.log.debug("info {s} data_list {any}", .{ info, data_list });
}

test 不支持 log

1
2
3
4
5
6
const std = @import("std");

test "debug log" {
// std.log.debug("log.debug", .{});
std.debug.print("debug.print\n", .{});
}

optional

用于表示一个值可能存在或者为 null

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const std = @import("std");

pub fn main() !void {
// 创建 option
var demo_option: ?u8 = null;
demo_option = 111;

// 修改 option
if (demo_option) |*value| {
std.log.debug("set data to option", .{});
value.* += 1;
}

if (demo_option == null) {
std.log.debug("null\n", .{});
} else {
const val = demo_option.?;
std.log.debug("not null, is {d}", .{val});
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const std = @import("std");

var _gpa: ?std.heap.GeneralPurposeAllocator(.{}) = null;

pub fn get_allocator() std.mem.Allocator {
if (_gpa == null) {
_gpa = std.heap.GeneralPurposeAllocator(.{}){};
}

return _gpa.?.allocator();
}

pub fn deinit() void {
if (_gpa != null) {
_ = _gpa.?.deinit();
_gpa = null;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
const std = @import("std");
const gpa = @import("gpa.zig");

pub fn main() !void {
const allocator = gpa.get_allocator();
defer gpa.deinit();

const formatted_str = try std.fmt.allocPrint(allocator, "测试 {}", .{42});
defer allocator.free(formatted_str);

std.debug.print("{s}\n", .{formatted_str});
}

string

zig 没有内置的 string 类型

1
2
3
4
5
6
7
8
9
10
11
12
13
const std = @import("std");

const String = []const u8;

pub fn demo() String {
return "aaa";
}

pub fn main() !void {
const aa = demo();
const bb = "bbb";
std.log.debug("result {s} {s}", .{ aa, bb });
}

struct

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const std = @import("std");

const MyStruct = struct {
x: f32,
y: f32,
z: f32 = 222.0,

fn demo(self: *MyStruct) void {
const tmp = self.x;
self.x = self.y;
self.y = tmp;
}
};

pub fn main() !void {
var aa = MyStruct{
.x = 10,
.y = 20,
};
aa.demo();
std.log.debug("aa = {}", .{aa});
}

thread

1
2
3
4
5
6
7
8
9
10
11
12
13
const std = @import("std");
const expect = std.testing.expect;

fn threadProc(thread_arg: u32) void {
std.log.debug("{d} in thread", .{thread_arg});
}

pub fn main() !void {
const thread = try std.Thread.spawn(.{}, threadProc, .{11111});
_ = thread;
// thread.join();
std.time.sleep(1 * std.time.ns_per_s);
}

union

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const std = @import("std");

const Result = union(enum) {
a: u8,
b: f32,
c: bool,
};

pub fn main() !void {
var value = Result{
.b = 1.5,
};
value.b = 3.156;
std.log.debug("{d:.2}", .{value.b});
}