基础
std.debug.print("Hello, world!\n", .{});
try std.io.getStdOut().writer().print("Hello, {s}!\n", .{"world"});
占位符:{}默认自适应类型
c
d(还有b,o,x,X):10进制整型和浮点
e:科学计数法
s
*:指针
//#:打印值的原始16进制--暂不知和*有啥区别
//?:调试信息
{[position][specifier]:[fill][alignment][width].[precision]}
| Name | Meaning |
|---|---|
| Position | The index of the argument that should be inserted |
| Specifier | A type-dependent formatting option |
| Fill | A single character used for padding |
| Alignment | One of three characters ‘<’, ‘^’ or ‘>’; these are for left, middle and right alignment |
| Width | The total width of the field (characters) |
| Precision | How many decimals a formatted number should have |
try expect(eql(u8,try bufPrint(&b, "{0s}{1s}{1s}", .{ "a", "b" }),"abb",));
try expect(eql(u8,try bufPrint(&b, "{d:.2}", .{3.14159}),"3.14",));
try expect(eql(u8,try bufPrint(&b, "{s:_^6}", .{"hi!"}),"_hi!__",));
log
/// The default log level is based on build mode.
pub const default_level: Level = switch (builtin.mode) {
.Debug => .debug,
.ReleaseSafe => .info,
.ReleaseFast, .ReleaseSmall => .err,
};
std.log.debug("A borderline useless debug log message", .{});//Debug
std.log.info("Flux capacitor is starting to overheat", .{});//Debug and Safe
std.log.warn("Warn", .{});//Debug and Safe
std.log.err("Error", .{});//Fast and Small
expect
const expect = @import("std").testing.expect;
test "if statement" {
const a = true;
var x: u16 = 0;
if (a) {
x += 1;
} else {
x += 2;
}
try expect(x == 1);
}
Value assignment ?赋值
//(const|var) identifier[: type] = value
var variable: u32 = 5000;
var inferred_variable = @as(u32, 5000);
var b: u32 = undefined;
const a = [5]u8{ 'h', 'e', 'l', 'l', 'o' };
const array = [_]u8{ 'h', 'e', 'l', 'l', 'o' };
const length = array.len; // 5
if
test "if statement expression" {
const a = true;
var x: u16 = 0;
x += if (a) 1 else 2;
try expect(x == 1);
}
switch
test "switch expression" {
var x: i8 = 10;
x = switch (x) {
-1...1 => -x,
10, 100 => @divExact(x, 10),
else => x,
};
try expect(x == 1);
}
unreachable
noreturn type, can coerce to any other type.
fn asciiToUpper(x: u8) u8 {
return switch (x) {
'a'...'z' => x + 'A' - 'a',
'A'...'Z' => x,
else => unreachable,
};
}
test "unreachable switch" {
try expect(asciiToUpper('a') == 'A');
try expect(asciiToUpper('A') == 'A');
}
//pub fn main() !void {//Debug,ReleaseSafe:reached unreachable code;Fast,Small:Segmentation fault
test "unreachable and -O" {//Debug,ReleaseSafe:reached unreachable code;Fast,Small:passed
var x: i8 = 11;
x = switch (x) {
-1...1 => -x,
10, 100 => @divExact(x, 10),
else => unreachable, //Debug,ReleaseSafe:reached unreachable code;Fast,Small:Segmentation fault
};
//try expect(x == 1);
std.debug.print("Hello, {s}!\n", .{"World"});
}
//src
const assert = std.debug.assert;
pub fn assert(ok: bool) void {
if (!ok) unreachable; // assertion failure
}
label
test "labelled blocks" {
const count = blk: {
var sum: u32 = 0;
var i: u32 = 0;
while (i < 10) : (i += 1) sum += i;
break :blk sum;
};
try expect(count == 45);
try expect(@TypeOf(count) == u32);
}
test "nested continue" {
var count: usize = 0;
outer: for ([_]i32{ 1, 2, 3, 4, 5, 6, 7, 8 }) |_| {
for ([_]i32{ 1, 2, 3, 4, 5 }) |_| {
count += 1;
continue :outer;
}
}
try expect(count == 8);
}
while
test "while with continue" {
var sum: u8 = 0;
var i: u8 = 0;
while (i <= 3) : (i += 1) {
if (i == 2) continue;//or break
sum += i;
}
try expect(sum == 4);
}
fn rangeHasNumber(begin: usize, end: usize, number: usize) bool {
var i = begin;
return while (i < end) : (i += 1) {
if (i == number) {
break true;
}
} else false;
}
test "while loop expression" {
try expect(rangeHasNumber(0, 10, 3));
}
for
test "for" {
//character literals are equivalent to integer literals
const string = [_]u8{ 'a', 'b', 'c' };
for (string) |character| {
_ = character;
}
for (string) |_| {}
for (string, 0..) |character, index| {
_ = character;
_ = index;
}
for (string, 0..) |_, index| {
_ = index;
}
}
test "inline for" {
const types = [_]type{ i32, f32, u8, bool };
var sum: usize = 0;
inline for (types) |T| sum += @sizeOf(T);
try expect(sum == 10);
}
fn
fn fibonacci(n: u16) u16 {
if (n == 0 or n == 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
test "function recursion" {
const x = fibonacci(10);
try expect(x == 55);
}
defer
test "multi defer" {
var x: f32 = 5;
{
defer x += 2;
defer x /= 2;
try expect(x == 5);
}
try expect(x == 4.5);
}
error:like enum
try x is a shortcut for x catch |err| return err
const FileOpenError = error{
AccessDenied,
OutOfMemory,
FileNotFound,
};
const AllocationError = error{OutOfMemory};
//一种自动提升
test "coerce error from a subset to a superset" {
const err: FileOpenError = AllocationError.OutOfMemory;
try expect(err == FileOpenError.OutOfMemory);
}
fn failingFunction() error{Oops}!void {
return error.Oops;
}
test "returning an error" {
failingFunction() catch |err| {
try expect(err == error.Oops);
return;
};
}
var problems: u32 = 98;
fn failFnCounter() error{Oops}!void {
errdefer problems += 1;
try failingFunction();
}
test "errdefer" {
failFnCounter() catch |err| {
try expect(err == error.Oops);
try expect(problems == 99);
return;
};
}
fn createFile() !void {
return error.AccessDenied;
}
test "inferred error set" {
//type coercion successfully takes place
const x: error{AccessDenied}!void = createFile();
//Zig does not let us ignore error unions via _ = x;
//we must unwrap it with "try", "catch", or "if" by any means
_ = x catch {};
}
//Error sets can be merged
const A = error{ NotDir, PathNotFound };
const B = error{ OutOfMemory, PathNotFound };
const C = A || B;
//anyerror is the global error set, which due to being the superset of all error sets, can have an error from any set coerced to it.
setRuntimeSafety
test "out of bounds" {
@setRuntimeSafety(false);
const a = [3]u8{ 1, 2, 3 };
var index: u8 = 5;
const b = a[index];
_ = b;
}
@Vector
test "Basic vector usage" {
// Vectors have a compile-time known length and base type.
const a = @Vector(4, i32){ 1, 2, 3, 4 };
const b = @Vector(4, i32){ 5, 6, 7, 8 };
// Math operations take place element-wise.
const c = a + b;
// Individual vector elements can be accessed using array indexing syntax.
try expectEqual(12, c[3]);
try expect(12 == c[3]);
try expect(std.meta.eql(c, @Vector(4, i32){ 6, 8, 10, 12 }));
}
test "Conversion between vectors, arrays, and slices" {
// Vectors and fixed-length arrays can be automatically assigned back and forth
var arr1: [4]f32 = [_]f32{ 1.1, 3.2, 4.5, 5.6 };
var vec: @Vector(4, f32) = arr1;
var arr2: [4]f32 = vec;
try expectEqual(arr1, arr2);
// You can also assign from a slice with comptime-known length to a vector using .*
try expect(@TypeOf(arr1[1..3]) == (*[2]f32));
try expect(@TypeOf(arr1[1..3].*) == ([2]f32));
const vec2: @Vector(2, f32) = arr1[1..3].*;
var slice: []const f32 = &arr1;
var offset: u32 = 1;
const vec3: @Vector(2, f32) = slice[offset..][0..2].*;
try expectEqual(slice[offset], vec2[0]);
try expectEqual(slice[offset + 1], vec2[1]);
try expectEqual(vec2, vec3);
}
pointer
- *T :ptr.*
- [*]T :索引,切片,偏移.ptr[i],ptr[start..end],ptr + x,ptr - x
- []T :索引,切片,长度.array_ptr[i],array_ptr[start..end],array_ptr.len
- *[N]T :索引,切片,长度.array_ptr[i],array_ptr[start..end],array_ptr.len 相当于数组的指针,.*解引用得数组,且可直接赋值给切片 try expect(@TypeOf(arr1[1..3]) == (*[2]f32));
try expect(@TypeOf(arr1[1..3].*) == ([2]f32));
try expect(@TypeOf(&arr1) == (*[4]f32));
var slice: []const f32 = &arr1;
var ptr: [*]const i32 = &array;
var ptr2: [*]const i32 = slice.ptr;
fn increment(num: *u8) void {
num.* += 1;
}
test "pointers" {
var x: u8 = 1;
increment(&x);
try expect(x == 2);
try expect(@sizeOf(usize) == @sizeOf(*u8));
try expect(@sizeOf(isize) == @sizeOf(*u8));
}
test "@intFromPtr and @ptrFromInt" {
const ptr: *i32 = @ptrFromInt(0xdeadbee0);
const addr = @intFromPtr(ptr);
try expect(@TypeOf(addr) == usize);
try expect(addr == 0xdeadbee0);
}
test "allowzero" {
var zero: usize = 0;
var ptr: *allowzero i32 = @ptrFromInt(zero);
try expect(@intFromPtr(ptr) == 0);
}
//[N:t]T, [:t]T, and [*:t]T, where t is a value of the child type T
test "sentinel termination" {
const terminated = [3:0]u8{ 3, 2, 1 };
try expect(terminated.len == 3);
try expect(@as(*const [4]u8, @ptrCast(&terminated))[3] == 0);
}
test "string literal" {
try expect(@TypeOf("hello") == *const [5:0]u8);
}
test "C string" {
const c_string: [*:0]const u8 = "hello";
const x: [*]const u8 = c_string;
_ = x;
var array: [5]u8 = undefined;
var i: usize = 0;
while (c_string[i] != 0) : (i += 1) {
array[i] = c_string[i];
}
}
test "sentinel terminated slicing" {
var x = [_:0]u8{255} ** 3;
const y = x[0..3 :0];
try expect(x[0] == 255);
try expect(x[3] == 0);
_ = y;
}
enum
const Value2 = enum(u32) {
hundred = 100,
thousand = 1000,
million = 1000000,
next,
};
test "set enum ordinal value" {
try expect(@intFromEnum(Value2.hundred) == 100);
try expect(@intFromEnum(Value2.thousand) == 1000);
try expect(@intFromEnum(Value2.million) == 1000000);
try expect(@intFromEnum(Value2.next) == 1000001);
}
const Suit = enum {
var count: u32 = 0;
clubs,
spades,
diamonds,
hearts,
pub fn isClubs(self: Suit) bool {
return self == Suit.clubs;
}
};
test "enum method" {
Suit.count += 1;
try expect(Suit.count == 1);
try expect(Suit.spades.isClubs() == Suit.isClubs(.spades));
}
struct
const Vec4 = struct { x: f32, y: f32, z: f32 = 0, w: f32 = undefined };
test "struct defaults" {
const my_vector = Vec4{
.x = 25,
.y = -50,
};
_ = my_vector;
}
const Stuff = struct {
x: i32,
y: i32,
fn swap(self: *Stuff) void {
const tmp = self.x;
self.x = self.y;
self.y = tmp;
}
};
test "automatic dereference" {
var thing = Stuff{ .x = 10, .y = 20 };
thing.swap();
try expect(thing.x == 20);
try expect(thing.y == 10);
}
test "tuple" {
const values = .{
@as(u32, 1234),
@as(f64, 12.34),
true,
"hi",
} ++ .{false} ** 2;
try expect(values[0] == 1234);
try expect(values[4] == false);
inline for (values, 0..) |v, i| {
if (i != 2) continue;
try expect(v);
}
try expect(values.len == 6);
try expect(values.@"3"[0] == 'h');
}
union
const Tag = enum { a, b, c };
const Tagged = union(Tag) { a: u8, b: f32, c: bool };
test "switch on tagged union" {
var value = Tagged{ .b = 1.5 };
switch (value) {
.a => |*byte| byte.* += 1,
.b => |*float| float.* *= 2,
.c => |*b| b.* = !b.*,
}
try expect(value.b == 3);
}
const Tagged2 = union(enum) { a: u8, b: f32, c: bool, none };
opaque
The typical usecase of opaque is to maintain type safety when interoperating with C code that does not expose complete type information.
const Window = opaque {
fn show(self: *Window) void {
show_window(self);
}
};
extern fn show_window(*Window) callconv(.C) void;
test "opaque with declarations" {
var main_window: *Window = undefined;
main_window.show();
}
Integer Rules ?提升(自动)
Overflows are detectable illegal behaviour,Zig provides overflow operators.
- +%=
- -%=
- *%=
const binary_mask: u64 = 0b1_1111_1111;
test "integer widening" {
const a: u8 = 250;
const b: u16 = a;
const c: u32 = b;
try expect(c == a);
}
test "@intCast" {
const x: u64 = 200;
const y = @as(u8, @intCast(x));
try expect(@TypeOf(y) == u8);
}
test "well defined overflow" {
var a: u8 = 255;
a +%= 1;
try expect(a == 0);
}
float ?提升
test "float widening" {
const a: f16 = 0;
const b: f32 = a;
const c: f128 = b;
try expect(c == @as(f128, a));
}
const another_float = 123.0;
const yet_another = 123.0e+77;
const inf = std.math.inf(f32);
const negative_inf = -std.math.inf(f64);
const nan = std.math.nan(f128);
test "int-float conversion" {
const a: i32 = 0;
const b = @as(f32, @floatFromInt(a)); //safe
const c = @as(i32, @intFromFloat(b)); //detectable illegal behaviour
try expect(c == a);
}
comptime
test "comptime blocks" {
var x = comptime fibonacci(10);
_ = x;
var y = comptime blk: {
break :blk fibonacci(10);
};
_ = y;
}
//comptime_int values coerce to any integer type that can hold them. They also coerce to floats. Character literals are of this type
test "comptime_int" {
const a = 12;
const b = a + 10;
const c: u4 = a;
_ = c;
const d: f32 = b;
_ = d;
}
//comptime_float is also available, which internally is an f128. These cannot be coerced to integers, even if they hold an integer value.
//array:++,** also comptime
//We can use the @Type function to create a type from a @typeInfo
fn GetBiggerInt(comptime T: type) type {
return @Type(.{
.Int = .{
.bits = @typeInfo(T).Int.bits + 1,
.signedness = @typeInfo(T).Int.signedness,
},
});
}
test "@Type" {
try expect(GetBiggerInt(u8) == u9);
try expect(GetBiggerInt(i31) == i32);
}
optional ?T:null or value of T
test "orelse" {
var a: ?f32 = null;
var b = a orelse 0;
try expect(b == 0);
try expect(@TypeOf(b) == f32);
}
test "orelse unreachable" {
const a: ?f32 = 5;
const b = a orelse unreachable;
const c = a.?;
try expect(b == c);
try expect(@TypeOf(c) == f32);
}
//we can “capture” its non-null value;有捕获才能做条件,否则x!=null
test "if optional payload capture" {
const a: ?i32 = 5;
if (a) |_| { //if (a != null) {
const value = a.?;
_ = value;
}
var b: ?i32 = 5;
if (b) |*value| {
value.* += 1;
}
try expect(b.? == 6);
}
payload capture
for value
option
error union
switch union
test "for capture" {
const x = [_]i8{ 1, 5, 120, -5 };
for (x) |v| try expect(@TypeOf(v) == i8);
}
test "for with pointer capture" {
var data = [_]u8{ 1, 2, 3 };
for (&data) |*byte| byte.* += 1;
try expect(std.mem.eql(u8, &data, &[_]u8{ 2, 3, 4 }));
}
//optional-if
//error union if
test "error union if" {
var ent_num: error{UnknownEntity}!u32 = 5;
if (ent_num) |entity| {
try expect(@TypeOf(entity) == u32);
try expect(entity == 5);
} else |err| {
_ = err catch {};
unreachable;
}
}
//while
var numbers_left2: u32 = undefined;
fn eventuallyErrorSequence() !u32 {
return if (numbers_left2 == 0) error.ReachedZero else blk: {
numbers_left2 -= 1;
break :blk numbers_left2;
};
}
test "while error union capture" {
var sum: u32 = 0;
numbers_left2 = 3;
while (eventuallyErrorSequence()) |value| {
sum += value;
} else |err| {
try expect(err == error.ReachedZero);
}
}
//switch
const Info = union(enum) {
a: u32,
b: []const u8,
c,
d: u32,
};
test "switch capture" {
var b = Info{ .a = 10 };
const x = switch (b) {
.b => |str| blk: {
try expect(@TypeOf(str) == []const u8);
break :blk 1;
},
.c => 2,
//if these are of the same type, they
//may be inside the same capture group
.a, .d => |num| blk: {
try expect(@TypeOf(num) == u32);
break :blk num * 2;
},
};
try expect(x == 20);
}
标准模式
allocator
test "allocation" {
const allocator = std.heap.page_allocator;
const memory = try allocator.alloc(u8, 100);
defer allocator.free(memory);
try expect(memory.len == 100);
try expect(@TypeOf(memory) == []u8);
}
test "allocator create/destroy" {
const byte = try std.heap.page_allocator.create(u8);
defer std.heap.page_allocator.destroy(byte);
byte.* = 128;
}
test "fixed buffer allocator" {
var buffer: [1000]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buffer);
const allocator = fba.allocator();
const memory = try allocator.alloc(u8, 100);
defer allocator.free(memory);
try expect(memory.len == 100);
try expect(@TypeOf(memory) == []u8);
}
//可多次分配,一次释放
test "arena allocator" {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = arena.allocator();
_ = try allocator.alloc(u8, 1);
_ = try allocator.alloc(u8, 10);
_ = try allocator.alloc(u8, 100);
}
//This is a safe allocator which can prevent double-free, use-after-free and can detect leaks. Safety checks and thread safety can be turned off via its configuration struct (left empty below).
test "GPA" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};//init,deinit;allocator:alloc,free
const allocator = gpa.allocator();
defer {
const deinit_status = gpa.deinit();
//fail test; can't try in defer as defer is executed after we return
if (deinit_status == .leak) expect(false) catch @panic("TEST FAIL");
}
const bytes = try allocator.alloc(u8, 100);
defer allocator.free(bytes);
}
//For high performance (but very few safety features!), std.heap.c_allocator
ArrayList
std.ArrayList(T) is similar to C++’s std::vector
//try list.append('o');
//try list.appendSlice(" World!");
//open = stack.pop()
const eql = std.mem.eql;
const ArrayList = std.ArrayList;
//const test_allocator = std.testing.allocator;
test "arraylist" {
//var list = ArrayList(u8).init(test_allocator);
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer {
const deinit_status = gpa.deinit();
if (deinit_status == .leak) expect(false) catch @panic("TEST FAIL");
}
const allocator = gpa.allocator();
var list = ArrayList(u8).init(allocator);
defer list.deinit();
try list.append('H');
try list.append('e');
try list.append('l');
try list.append('l');
try list.append('o');
try list.appendSlice(" World!");
try expect(eql(u8, list.items, "Hello World!"));
}
test "stack" {
const string = "(()())";
var stack = std.ArrayList(usize).init(
test_allocator,
);
defer stack.deinit();
const Pair = struct { open: usize, close: usize };
var pairs = std.ArrayList(Pair).init(
test_allocator,
);
defer pairs.deinit();
for (string, 0..) |char, i| {
if (char == '(') try stack.append(i);
if (char == ')')
try pairs.append(.{
.open = stack.pop(),
.close = i,
});
}
for (pairs.items, 0..) |pair, i| {
try expect(std.meta.eql(pair, switch (i) {
0 => Pair{ .open = 1, .close = 2 },
1 => Pair{ .open = 3, .close = 4 },
2 => Pair{ .open = 0, .close = 5 },
else => unreachable,
}));
}
}
HashMap
//m.put(k,v)
//const old = try map.fetchPut(255, 100);
//try expect(map.get(255).? == 100);
test "hashing" {
const Point = struct { x: i32, y: i32 };
var map = std.AutoHashMap(u32, Point).init(
test_allocator,
);
defer map.deinit();
try map.put(1525, .{ .x = 1, .y = -4 });
try map.put(1550, .{ .x = 2, .y = -3 });
try map.put(1575, .{ .x = 3, .y = -2 });
try map.put(1600, .{ .x = 4, .y = -1 });
try expect(map.count() == 4);
var sum = Point{ .x = 0, .y = 0 };
var iterator = map.iterator();
while (iterator.next()) |entry| {
sum.x += entry.value_ptr.x;
sum.y += entry.value_ptr.y;
}
try expect(sum.x == 10);
try expect(sum.y == -10);
}
test "fetchPut" {
var map = std.AutoHashMap(u8, f32).init(
test_allocator,
);
defer map.deinit();
try map.put(255, 10);
const old = try map.fetchPut(255, 100);
try expect(old.?.value == 10);
try expect(map.get(255).? == 100);
}
以下了解即可,随用随取
std.mem.sort
std.sort.sort has a best case of O(n), and an average and worst case of O(n*log(n)).
test "sorting" {
var data = [_]u8{ 10, 240, 0, 0, 10, 5 };
std.mem.sort(u8, &data, {}, comptime std.sort.asc(u8));
try expect(eql(u8, &data, &[_]u8{ 0, 0, 5, 10, 10, 240 }));
std.mem.sort(u8, &data, {}, comptime std.sort.desc(u8));
try expect(eql(u8, &data, &[_]u8{ 240, 10, 10, 5, 0, 0 }));
}
iterator
test "split iterator" {
const text = "robust, optimal, reusable, maintainable, ";
var iter = std.mem.split(u8, text, ", ");
try expect(eql(u8, iter.next().?, "robust"));
try expect(eql(u8, iter.next().?, "optimal"));
try expect(eql(u8, iter.next().?, "reusable"));
try expect(eql(u8, iter.next().?, "maintainable"));
try expect(eql(u8, iter.next().?, ""));
try expect(iter.next() == null);
}
test "iterator looping" {
var iter = (try std.fs.cwd().openIterableDir(
".",
.{},
)).iterate();
var file_count: usize = 0;
while (try iter.next()) |entry| {
if (entry.kind == .file) file_count += 1;
}
try expect(file_count > 0);
}
//try to implement a custom iterator
Filesystem
test "createFile, write, seekTo, read" {
const file = try std.fs.cwd().createFile(
"junk_file.txt",
.{ .read = true },
);
defer file.close();
const bytes_written = try file.writeAll("Hello File!");
_ = bytes_written;
var buffer: [100]u8 = undefined;
try file.seekTo(0);
const bytes_read = try file.readAll(&buffer);
try expect(eql(u8, buffer[0..bytes_read], "Hello File!"));
}
test "file stat" {
const file = try std.fs.cwd().createFile(
"junk_file2.txt",
.{ .read = true },
);
defer file.close();
const stat = try file.stat();
try expect(stat.size == 0);
try expect(stat.kind == .file);
try expect(stat.ctime <= std.time.nanoTimestamp());
try expect(stat.mtime <= std.time.nanoTimestamp());
try expect(stat.atime <= std.time.nanoTimestamp());
}
test "make dir" {
try std.fs.cwd().makeDir("test-tmp");
var iter_dir = try std.fs.cwd().openIterableDir(
"test-tmp",
.{},
);
defer {
iter_dir.close();
std.fs.cwd().deleteTree("test-tmp") catch unreachable;
}
_ = try iter_dir.dir.createFile("x", .{});
_ = try iter_dir.dir.createFile("y", .{});
_ = try iter_dir.dir.createFile("z", .{});
var file_count: usize = 0;
var iter = iter_dir.iterate();
while (try iter.next()) |entry| {
if (entry.kind == .file) file_count += 1;
}
try expect(file_count == 3);
}
json
test "json parse" {
const parsed = try std.json.parseFromSlice(
Place,
test_allocator,
\\{ "lat": 40.684540, "long": -74.401422 }
,
.{},
);
defer parsed.deinit();
const place = parsed.value;
try expect(place.lat == 40.684540);
try expect(place.long == -74.401422);
}
test "json stringify" {
const x = Place{
.lat = 51.997664,
.long = -0.740687,
};
var buf: [100]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buf);
var string = std.ArrayList(u8).init(fba.allocator());
try std.json.stringify(x, .{}, string.writer());
try expect(eql(u8, string.items,
\\{"lat":5.199766540527344e+01,"long":-7.406870126724243e-01}
));
}
Reader amd Writer
test "io writer usage" {
var list = ArrayList(u8).init(test_allocator);
defer list.deinit();
const bytes_written = try list.writer().write(
"Hello World!",
);
try expect(bytes_written == 12);
try expect(eql(u8, list.items, "Hello World!"));
}
test "io reader usage" {
const message = "Hello File!";
const file = try std.fs.cwd().createFile(
"junk_file2.txt",
.{ .read = true },
);
defer file.close();
try file.writeAll(message);
try file.seekTo(0);
const contents = try file.reader().readAllAlloc(
test_allocator,
message.len,
);
defer test_allocator.free(contents);
try expect(eql(u8, contents, message));
}
fn nextLine(reader: anytype, buffer: []u8) !?[]const u8 {
var line = (try reader.readUntilDelimiterOrEof(
buffer,
'\n',
)) orelse return null;
// trim annoying windows-only carriage return character
if (@import("builtin").os.tag == .windows) {
return std.mem.trimRight(u8, line, "\r");
} else {
return line;
}
}
test "read until next line" {
const stdout = std.io.getStdOut();
const stdin = std.io.getStdIn();
try stdout.writeAll(
\\ Enter your name:
);
var buffer: [100]u8 = undefined;
const input = (try nextLine(stdin.reader(), &buffer)).?;
try stdout.writer().print(
"Your name is: \"{s}\"\n",
.{input},
);
}
// Don't create a type like this! Use an
// arraylist with a fixed buffer allocator
const MyByteList = struct {
data: [100]u8 = undefined,
items: []u8 = &[_]u8{},
const Writer = std.io.Writer(
*MyByteList,
error{EndOfBuffer},
appendWrite,
);
fn appendWrite(
self: *MyByteList,
data: []const u8,
) error{EndOfBuffer}!usize {
if (self.items.len + data.len > self.data.len) {
return error.EndOfBuffer;
}
std.mem.copy(
u8,
self.data[self.items.len..],
data,
);
self.items = self.data[0 .. self.items.len + data.len];
return data.len;
}
fn writer(self: *MyByteList) Writer {
return .{ .context = self };
}
};
test "custom writer" {
var bytes = MyByteList{};
_ = try bytes.writer().write("Hello");
_ = try bytes.writer().write(" Writer!");
try expect(eql(u8, bytes.items, "Hello Writer!"));
}
rand
test "random numbers" {
var prng = std.rand.DefaultPrng.init(blk: {
var seed: u64 = undefined;
try std.os.getrandom(std.mem.asBytes(&seed));
break :blk seed;
});
const rand = prng.random();
const a = rand.float(f32);
const b = rand.boolean();
const c = rand.int(u8);
const d = rand.intRangeAtMost(u8, 0, 255);
//suppress unused constant compile error
_ = .{ a, b, c, d };
}
test "crypto random numbers" {
const rand = std.crypto.random;
const a = rand.float(f32);
const b = rand.boolean();
const c = rand.int(u8);
const d = rand.intRangeAtMost(u8, 0, 255);
//suppress unused constant compile error
_ = .{ a, b, c, d };
}
std.Thread
//While Zig provides more advanced ways of writing concurrent and parallel code, std.Thread is available for making use of OS threads.
//Threads, however, aren’t particularly useful without strategies for thread safety.
fn ticker(step: u8) void {
while (true) {
std.time.sleep(1 * std.time.ns_per_s);
tick += @as(isize, step);
}
}
var tick: isize = 0;
test "threading" {
var thread = try std.Thread.spawn(.{}, ticker, .{@as(u8, 1)});
_ = thread;
try expect(tick == 0);
std.time.sleep(3 * std.time.ns_per_s / 2);
try expect(tick == 1);
}
构建系统
build command
Linking libc can be done via the command line via -lc, or via build.zig using exe.linkLibC();.
# zig [build-exe|build-lib|run|test] [options] [files]
zig build-exe -lc -target x86_64-linux-musl -O ReleaseFast -fstrip hi.zig
# 生成文档-femit-docs,build.zig也可docs.emit_docs = .emit;
# zig build-lib -femit-docs x.zig -target native-windows
# zig build install -h 仅在build.zig存在时有效
zig build -Dtarget=x86_64-linux-gnu -Doptimize=ReleaseFast
working with c
bool,char,long double
| Type | C Equivalent | Minimum Size(bits) |
|---|---|---|
| bool | bool | 1/8 |
| c_char | char | 8 |
| c_short | short | 16 |
| c_ushort | unsigned short | 16 |
| c_int | int | 16 |
| c_uint | unsigned int | 16 |
| c_long | long | 32 |
| c_ulong | unsigned long | 32 |
| c_longlong | long long | 64 |
| c_ulonglong | unsigned long long | 64 |
| c_longdouble | long double | N/A |
| anyopaque | void | N/A |
extern struct
const expect = @import("std").testing.expect;
const Data = extern struct { a: i32, b: u8, c: f32, d: bool, e: bool };
test "hmm" {
const x = Data{
.a = 10005,
.b = 42,
.c = -10.5,
.d = false,
.e = true,
};
const z = @as([*]const u8, @ptrCast(&x));
try expect(@as(*const i32, @ptrCast(@alignCast(z))).* == 10005);
try expect(@as(*const u8, @ptrCast(@alignCast(z + 4))).* == 42);
try expect(@as(*const f32, @ptrCast(@alignCast(z + 8))).* == -10.5);
try expect(@as(*const bool, @ptrCast(@alignCast(z + 12))).* == false);
try expect(@as(*const bool, @ptrCast(@alignCast(z + 13))).* == true);
}
align
const b1: u64 align(1) = 100;
const b2 align(1) = @as(u64, 100);
test "aligned pointers" {
const a: u32 align(8) = 5;
try expect(@TypeOf(&a) == *align(8) const u32);
}
fn total(a: *align(64) const [64]u8) u32 {
var sum: u32 = 0;
for (a) |elem| sum += elem;
return sum;
}
test "passing aligned data" {
const x align(128) = [1]u8{10} ** 64;
try expect(total(&x) == 640);
}
test "bit aligned pointers" {
var x = MovementState{
.running = false,
.crouching = false,
.jumping = false,
.in_air = false,
};
const running = &x.running;
running.* = true;
const crouching = &x.crouching;
crouching.* = true;
try expect(@TypeOf(running) == *align(1:0:1) bool);
try expect(@TypeOf(crouching) == *align(1:1:1) bool);
try expect(@import("std").meta.eql(x, .{
.running = true,
.crouching = true,
.jumping = false,
.in_air = false,
}));
}
packed struct
By default, all struct fields in Zig are naturally aligned to that of @alignOf(FieldType) (the ABI size).
const MovementState = packed struct {
running: bool,
crouching: bool,
jumping: bool,
in_air: bool,
};
test "packed struct size" {
try expect(@sizeOf(MovementState) == 1);
try expect(@bitSizeOf(MovementState) == 4);
const state = MovementState{
.running = true,
.crouching = true,
.jumping = true,
.in_air = true,
};
_ = state;
}
test "bit aligned pointers" {
var x = MovementState{
.running = false,
.crouching = false,
.jumping = false,
.in_air = false,
};
const running = &x.running;
running.* = true;
const crouching = &x.crouching;
crouching.* = true;
try expect(@TypeOf(running) == *align(1:0:1) bool);
try expect(@TypeOf(crouching) == *align(1:1:1) bool);
try expect(@import("std").meta.eql(x, .{
.running = true,
.crouching = true,
.jumping = false,
.in_air = false,
}));
}
cimport
@cImport(expression) type
cImport is only available when linking libc.
Similar to @import, this returns a struct type with declarations. It is typically recommended to only use one instance of @cImport in an application to avoid symbol collisions;
const c = @cImport({
@cDefine("NDEBUG", builtin.mode == .ReleaseFast);
if (something) {
@cDefine("_GNU_SOURCE", {});
}
@cInclude("stdlib.h");
if (something) {
@cUndef("_GNU_SOURCE");
}
@cInclude("soundio.h");
});
const c = @cImport({
// See https://github.com/ziglang/zig/issues/515
@cDefine("_NO_CRT_STDIO_INLINE", "1");
@cInclude("stdio.h");
});
pub fn main() void {
_ = c.printf("hello\n");
}