const print = @import("std").debug.print;
pubfnmain() void {
// Comments in Zig start with "//" and end at the next LF byte (end of line).// The line below is a comment and won't be executed.//print("Hello?", .{});
print("Hello, world!\n", .{}); // another comment
}
/// A structure for storing a timestamp, with nanosecond precision (this is a/// multiline doc comment).const Timestamp = struct {
/// The number of seconds since the epoch (this is also a doc comment).
seconds: i64, // signed so we can represent pre-1970 (not a doc comment)/// The number of nanoseconds past the second (doc comment again).
nanos: u32,
/// Returns a `Timestamp` struct representing the Unix epoch; that is, the/// moment of 1970 Jan 1 00:00:00 UTC (this is a doc comment too).pubfnunixEpoch() Timestamp {
return Timestamp{
.seconds = 0,
.nanos = 0,
};
}
};
$ zig build-obj invalid_doc-comment.zig/home/andy/dev/zig/doc/langref/invalid_doc-comment.zig:1:16: error: expected type expression, found 'a document comment'
/// doc-comment
^
unattached_doc-comment.zig
pubfnmain() void {}
/// End of file
Shell
$ zig build-obj unattached_doc-comment.zig/home/andy/dev/zig/doc/langref/unattached_doc-comment.zig:3:1: error: unattached documentation comment
/// End of file
^~~~~~~~~~~~~~~
//! This module provides functions for retrieving the current date and//! time with varying degrees of precision and accuracy. It does not//! depend on libc, but will use functions from it if available.const S = struct {
//! Top level comments are allowed inside a container other than a module,//! but it is not very useful. Currently, when producing the package//! documentation, these comments are ignored.
};
const x = 1234;
fnfoo() void {
// It works at file scope as well as inside functions.const y = 5678;
// Once assigned, an identifier cannot be changed.
y += 1;
}
pubfnmain() void {
foo();
}
Shell
$ zig build-exe constant_identifier_cannot_change.zig/home/andy/dev/zig/doc/langref/constant_identifier_cannot_change.zig:8:7: error: cannot assign to constant
y += 1;
~~^~~~
referenced by:
main: /home/andy/dev/zig/doc/langref/constant_identifier_cannot_change.zig:12:8
posixCallMainAndExit: /home/andy/dev/zig/lib/std/start.zig:651:22
4 reference(s) hidden; use '-freference-trace=6' to see all references
const print = @import("std").debug.print;
pubfnmain() void {
var x: u32 = undefined;
var y: u32 = undefined;
var z: u32 = undefined;
const tuple = .{ 1, 2, 3 };
x, y, z = tuple;
print("tuple: x = {}, y = {}, z = {}\n", .{x, y, z});
const array = [_]u32{ 4, 5, 6 };
x, y, z = array;
print("array: x = {}, y = {}, z = {}\n", .{x, y, z});
const vector: @Vector(3, u32) = .{ 7, 8, 9 };
x, y, z = vector;
print("vector: x = {}, y = {}, z = {}\n", .{x, y, z});
}
Shell
$ zig build-exe destructuring_to_existing.zig
$ ./destructuring_to_existing
tuple: x = 1, y = 2, z = 3
array: x = 4, y = 5, z = 6
vector: x = 7, y = 8, z = 9
const print = @import("std").debug.print;
pubfnmain() void {
var x: u32 = undefined;
const tuple = .{ 1, 2, 3 };
x, var y : u32, const z = tuple;
print("x = {}, y = {}, z = {}\n", .{x, y, z});
// y is mutable
y = 100;
// You can use _ to throw away unwanted values.
_, x, _ = tuple;
print("x = {}", .{x});
}
Shell
$ zig build-exe destructuring_mixed.zig
$ ./destructuring_mixed
x = 1, y = 2, z = 3
x = 2
解构表达式可以加上 comptime 关键字前缀,在这种情况下,整个解构表达式会在编译期求值。所有声明的 var 都将是 comptimevar,并且所有表达式(包括结果位置和赋值表达式)都在编译期求值。
const std = @import("std");
test"expect addOne adds one to 41" {
// The Standard Library contains useful functions to help create tests.// `expect` is a function that verifies its argument is true.// It will return an error if its argument is false to indicate a failure.// `try` is used to return an error to the test runner to notify it that the test failed.try std.testing.expect(addOne(41) == 42);
}
test addOne {
// A test name can also be written using an identifier.// This is a doctest, and serves as documentation for `addOne`.try std.testing.expect(addOne(41) == 42);
}
/// The function `addOne` adds one to the number given as its argument.fnaddOne(number: i32) i32 {
return number + 1;
}
Shell
$ zig test testing_introduction.zig
1/2 testing_introduction.test.expect addOne adds one to 41...OK
2/2 testing_introduction.decltest.addOne...OK
All 2 tests passed.
const std = @import("std");
test"expect this to fail" {
try std.testing.expect(false);
}
test"expect this to succeed" {
try std.testing.expect(true);
}
Shell
$ zig test testing_failure.zig
1/2 testing_failure.test.expect this to fail...FAIL (TestUnexpectedResult)
/home/andy/dev/zig/lib/std/testing.zig:580:14: 0x104890f in expect (test)
if (!ok) return error.TestUnexpectedResult;
^/home/andy/dev/zig/doc/langref/testing_failure.zig:4:5: 0x10489a5 in test.expect this to fail (test)
try std.testing.expect(false);
^
2/2 testing_failure.test.expect this to succeed...OK
1 passed; 0 skipped; 1 failed.
error: the following test command failed with exit code 1:
/home/andy/dev/zig/.zig-cache/o/37b2473244f3a3046525f6f7b1b36e1a/test --seed=0xd1a8ca45
const std = @import("std");
test"expectEqual demo" {
const expected: i32 = 42;
const actual = 42;
// The first argument to `expectEqual` is the known, expected, result.// The second argument is the result of some expression.// The actual's type is casted to the type of expected.try std.testing.expectEqual(expected, actual);
}
test"expectError demo" {
const expected_error = error.DemoError;
const actual_error_union: anyerror!void = error.DemoError;
// `expectError` will fail when the actual error is different than// the expected error.try std.testing.expectError(expected_error, actual_error_union);
}
Shell
$ zig test testing_namespace.zig
1/2 testing_namespace.test.expectEqual demo...OK
2/2 testing_namespace.test.expectError demo...OK
All 2 tests passed.
const std = @import("std");
const expect = std.testing.expect;
test"comptime vars" {
var x: i32 = 1;
comptimevar y: i32 = 1;
x += 1;
y += 1;
try expect(x == 2);
try expect(y == 2);
if (y != 2) {
// This compile error never triggers because y is a comptime variable,// and so `y != 2` is a comptime value, and this if is statically evaluated.@compileError("wrong y value");
}
}
Shell
$ zig test test_comptime_variables.zig
1/1 test_comptime_variables.test.comptime vars...OK
All 1 tests passed.
const expect = @import("std").testing.expect;
const assert = @import("std").debug.assert;
const mem = @import("std").mem;
// array literalconst message = [_]u8{ 'h', 'e', 'l', 'l', 'o' };
// alternative initialization using result locationconst alt_message: [5]u8 = .{ 'h', 'e', 'l', 'l', 'o' };
comptime {
assert(mem.eql(u8, &message, &alt_message));
}
// get the size of an arraycomptime {
assert(message.len == 5);
}
// A string literal is a single-item pointer to an array.const same_message = "hello";
comptime {
assert(mem.eql(u8, &message, same_message));
}
test"iterate over an array" {
var sum: usize = 0;
for (message) |byte| {
sum += byte;
}
try expect(sum == 'h' + 'e' + 'l' * 2 + 'o');
}
// modifiable arrayvar some_integers: [100]i32 = undefined;
test"modify an array" {
for (&some_integers, 0..) |*item, i| {
item.* = @intCast(i);
}
try expect(some_integers[10] == 10);
try expect(some_integers[99] == 99);
}
// array concatenation works if the values are known// at compile timeconst part_one = [_]i32{ 1, 2, 3, 4 };
const part_two = [_]i32{ 5, 6, 7, 8 };
const all_of_it = part_one ++ part_two;
comptime {
assert(mem.eql(i32, &all_of_it, &[_]i32{ 1, 2, 3, 4, 5, 6, 7, 8 }));
}
// remember that string literals are arraysconst hello = "hello";
const world = "world";
const hello_world = hello ++ " " ++ world;
comptime {
assert(mem.eql(u8, hello_world, "hello world"));
}
// ** does repeating patternsconst pattern = "ab" ** 3;
comptime {
assert(mem.eql(u8, pattern, "ababab"));
}
// initialize an array to zeroconst all_zero = [_]u16{0} ** 10;
comptime {
assert(all_zero.len == 10);
assert(all_zero[5] == 0);
}
// use compile-time code to initialize an arrayvar fancy_array = init: {
var initial_value: [10]Point = undefined;
for (&initial_value, 0..) |*pt, i| {
pt.* = Point{
.x = @intCast(i),
.y = @intCast(i * 2),
};
}
break :init initial_value;
};
const Point = struct {
x: i32,
y: i32,
};
test"compile-time array initialization" {
try expect(fancy_array[4].x == 4);
try expect(fancy_array[4].y == 8);
}
// call a function to initialize an arrayvar more_points = [_]Point{makePoint(3)} ** 10;
fnmakePoint(x: i32) Point {
return Point{
.x = x,
.y = x * 2,
};
}
test"array initialization with function calls" {
try expect(more_points[4].x == 3);
try expect(more_points[4].y == 6);
try expect(more_points.len == 10);
}
Shell
$ zig test test_arrays.zig
1/4 test_arrays.test.iterate over an array...OK
2/4 test_arrays.test.modify an array...OK
3/4 test_arrays.test.compile-time array initialization...OK
4/4 test_arrays.test.array initialization with function calls...OK
All 4 tests passed.
const std = @import("std");
const expectEqual = std.testing.expectEqual;
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(6, c[0]);
try expectEqual(8, c[1]);
try expectEqual(10, c[2]);
try expectEqual(12, c[3]);
}
test"Conversion between vectors, arrays, and slices" {
// Vectors and fixed-length arrays can be automatically assigned back and forthconst arr1: [4]f32 = [_]f32{ 1.1, 3.2, 4.5, 5.6 };
const vec: @Vector(4, f32) = arr1;
const arr2: [4]f32 = vec;
try expectEqual(arr1, arr2);
// You can also assign from a slice with comptime-known length to a vector using .*const vec2: @Vector(2, f32) = arr1[1..3].*;
const slice: []constf32 = &arr1;
var offset: u32 = 1; // var to make it runtime-known
_ = &offset; // suppress 'var is never mutated' error// To extract a comptime-known length from a runtime-known offset,// first extract a new slice from the starting offset, then an array of// comptime-known lengthconst 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);
}
Shell
$ zig test test_vector.zig
1/2 test_vector.test.Basic vector usage...OK
2/2 test_vector.test.Conversion between vectors, arrays, and slices...OK
All 2 tests passed.
const expect = @import("std").testing.expect;
test"address of syntax" {
// Get the address of a variable:const x: i32 = 1234;
const x_ptr = &x;
// Dereference a pointer:try expect(x_ptr.* == 1234);
// When you get the address of a const variable, you get a const single-item pointer.try expect(@TypeOf(x_ptr) == *consti32);
// If you want to mutate the value, you'd need an address of a mutable variable:var y: i32 = 5678;
const y_ptr = &y;
try expect(@TypeOf(y_ptr) == *i32);
y_ptr.* += 1;
try expect(y_ptr.* == 5679);
}
test"pointer array access" {
// Taking an address of an individual element gives a// single-item pointer. This kind of pointer// does not support pointer arithmetic.var array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
const ptr = &array[2];
try expect(@TypeOf(ptr) == *u8);
try expect(array[2] == 3);
ptr.* += 1;
try expect(array[2] == 4);
}
test"slice syntax" {
// Get a pointer to a variable:var x: i32 = 1234;
const x_ptr = &x;
// Convert to array pointer using slice syntax:const x_array_ptr = x_ptr[0..1];
try expect(@TypeOf(x_array_ptr) == *[1]i32);
// Coerce to many-item pointer:const x_many_ptr: [*]i32 = x_array_ptr;
try expect(x_many_ptr[0] == 1234);
}
Shell
$ zig test test_single_item_pointer.zig
1/3 test_single_item_pointer.test.address of syntax...OK
2/3 test_single_item_pointer.test.pointer array access...OK
3/3 test_single_item_pointer.test.slice syntax...OK
All 3 tests passed.
const expect = @import("std").testing.expect;
test"pointer arithmetic with many-item pointer" {
const array = [_]i32{ 1, 2, 3, 4 };
var ptr: [*]consti32 = &array;
try expect(ptr[0] == 1);
ptr += 1;
try expect(ptr[0] == 2);
// slicing a many-item pointer without an end is equivalent to// pointer arithmetic: `ptr[start..] == ptr + start`try expect(ptr[1..] == ptr + 1);
// subtraction between any two pointers except slices based on element size is supportedtry expect(&ptr[1] - &ptr[0] == 1);
}
test"pointer arithmetic with slices" {
var array = [_]i32{ 1, 2, 3, 4 };
var length: usize = 0; // var to make it runtime-known
_ = &length; // suppress 'var is never mutated' errorvar slice = array[length..array.len];
try expect(slice[0] == 1);
try expect(slice.len == 4);
slice.ptr += 1;
// now the slice is in an bad state since len has not been updatedtry expect(slice[0] == 2);
try expect(slice.len == 4);
}
Shell
$ zig test test_pointer_arithmetic.zig
1/2 test_pointer_arithmetic.test.pointer arithmetic with many-item pointer...OK
2/2 test_pointer_arithmetic.test.pointer arithmetic with slices...OK
All 2 tests passed.
$ zig test test_integer_pointer_conversion.zig
1/1 test_integer_pointer_conversion.test.@intFromPtr and @ptrFromInt...OK
All 1 tests passed.
Zig 能够在编译期代码中保留内存地址,只要指针从不被解引用。
test_comptime_pointer_conversion.zig
const expect = @import("std").testing.expect;
test"comptime @ptrFromInt" {
comptime {
// Zig is able to do this at compile-time, as long as// ptr is never dereferenced.const ptr: *i32 = @ptrFromInt(0xdeadbee0);
const addr = @intFromPtr(ptr);
try expect(@TypeOf(addr) == usize);
try expect(addr == 0xdeadbee0);
}
}
Shell
$ zig test test_comptime_pointer_conversion.zig
1/1 test_comptime_pointer_conversion.test.comptime @ptrFromInt...OK
All 1 tests passed.
const std = @import("std");
const expect = std.testing.expect;
test"pointer casting" {
const bytes align(@alignOf(u32)) = [_]u8{ 0x12, 0x12, 0x12, 0x12 };
const u32_ptr: *constu32 = @ptrCast(&bytes);
try expect(u32_ptr.* == 0x12121212);
// Even this example is contrived - there are better ways to do the above than// pointer casting. For example, using a slice narrowing cast:const u32_value = std.mem.bytesAsSlice(u32, bytes[0..])[0];
try expect(u32_value == 0x12121212);
// And even another way, the most straightforward way to do it:try expect(@as(u32, @bitCast(bytes)) == 0x12121212);
}
test"pointer child type" {
// pointer types have a `child` field which tells you the type they point to.try expect(@typeInfo(*u32).pointer.child == u32);
}
Shell
$ zig test test_pointer_casting.zig
1/2 test_pointer_casting.test.pointer casting...OK
2/2 test_pointer_casting.test.pointer child type...OK
All 2 tests passed.
切片是包含指针和长度的结构。数组和切片之间的区别在于,数组的长度是类型的一部分并在编译时已知,而切片的长度在运行时已知。两者都可以通过 len 字段访问。
test_basic_slices.zig
const expect = @import("std").testing.expect;
const expectEqualSlices = @import("std").testing.expectEqualSlices;
test"basic slices" {
var array = [_]i32{ 1, 2, 3, 4 };
var known_at_runtime_zero: usize = 0;
_ = &known_at_runtime_zero;
const slice = array[known_at_runtime_zero..array.len];
// alternative initialization using result locationconst alt_slice: []consti32 = &.{ 1, 2, 3, 4 };
try expectEqualSlices(i32, slice, alt_slice);
try expect(@TypeOf(slice) == []i32);
try expect(&slice[0] == &array[0]);
try expect(slice.len == array.len);
// If you slice with comptime-known start and end positions, the result is// a pointer to an array, rather than a slice.const array_ptr = array[0..array.len];
try expect(@TypeOf(array_ptr) == *[array.len]i32);
// You can perform a slice-by-length by slicing twice. This allows the compiler// to perform some optimisations like recognising a comptime-known length when// the start position is only known at runtime.var runtime_start: usize = 1;
_ = &runtime_start;
const length = 2;
const array_ptr_len = array[runtime_start..][0..length];
try expect(@TypeOf(array_ptr_len) == *[length]i32);
// Using the address-of operator on a slice gives a single-item pointer.try expect(@TypeOf(&slice[0]) == *i32);
// Using the `ptr` field gives a many-item pointer.try expect(@TypeOf(slice.ptr) == [*]i32);
try expect(@intFromPtr(slice.ptr) == @intFromPtr(&slice[0]));
// Slices have array bounds checking. If you try to access something out// of bounds, you'll get a safety check failure:
slice[10] += 1;
// Note that `slice.ptr` does not invoke safety checking, while `&slice[0]`// asserts that the slice has len > 0.// Empty slices can be created like this:const empty1 = &[0]u8{};
// If the type is known you can use this short hand:const empty2: []u8 = &.{};
try expect(empty1.len == 0);
try expect(empty2.len == 0);
// A zero-length initialization can always be used to create an empty slice, even if the slice is mutable.// This is because the pointed-to data is zero bits long, so its immutability is irrelevant.
}
Shell
$ zig test test_basic_slices.zig
1/1 test_basic_slices.test.basic slices...thread 200412 panic: index out of bounds: index 10, len 4
/home/andy/dev/zig/doc/langref/test_basic_slices.zig:41:10: 0x104b511 in test.basic slices (test)
slice[10] += 1;
^/home/andy/dev/zig/lib/compiler/test_runner.zig:214:25: 0x10f2925 in mainTerminal (test)
if (test_fn.func()) |_| {
^/home/andy/dev/zig/lib/compiler/test_runner.zig:62:28: 0x10ead0d in main (test)
return mainTerminal();
^/home/andy/dev/zig/lib/std/start.zig:651:22: 0x10ea182 in posixCallMainAndExit (test)
root.main();
^/home/andy/dev/zig/lib/std/start.zig:271:5: 0x10e9d5d in _start (test)
asm volatile (switch (native_arch) {
^???:?:?: 0x0 in ??? (???)
error: the following test command crashed:
/home/andy/dev/zig/.zig-cache/o/59dc8382d8140cff64d1ea721c83a213/test --seed=0x2837bf4f
这是我们更倾向于切片而非指针的一个原因。
test_slices.zig
const std = @import("std");
const expect = std.testing.expect;
const mem = std.mem;
const fmt = std.fmt;
test"using slices for strings" {
// Zig has no concept of strings. String literals are const pointers// to null-terminated arrays of u8, and by convention parameters// that are "strings" are expected to be UTF-8 encoded slices of u8.// Here we coerce *const [5:0]u8 and *const [6:0]u8 to []const u8const hello: []constu8 = "hello";
const world: []constu8 = "世界";
var all_together: [100]u8 = undefined;
// You can use slice syntax with at least one runtime-known index on an// array to convert an array into a slice.var start: usize = 0;
_ = &start;
const all_together_slice = all_together[start..];
// String concatenation example.const hello_world = try fmt.bufPrint(all_together_slice, "{s} {s}", .{ hello, world });
// Generally, you can use UTF-8 and not worry about whether something is a// string. If you don't need to deal with individual characters, no need// to decode.try expect(mem.eql(u8, hello_world, "hello 世界"));
}
test"slice pointer" {
var array: [10]u8 = undefined;
const ptr = &array;
try expect(@TypeOf(ptr) == *[10]u8);
// A pointer to an array can be sliced just like an array:var start: usize = 0;
var end: usize = 5;
_ = .{ &start, &end };
const slice = ptr[start..end];
// The slice is mutable because we sliced a mutable pointer.try expect(@TypeOf(slice) == []u8);
slice[2] = 3;
try expect(array[2] == 3);
// Again, slicing with comptime-known indexes will produce another pointer// to an array:const ptr2 = slice[2..3];
try expect(ptr2.len == 1);
try expect(ptr2[0] == 3);
try expect(@TypeOf(ptr2) == *[1]u8);
}
Shell
$ zig test test_slices.zig
1/2 test_slices.test.using slices for strings...OK
2/2 test_slices.test.slice pointer...OK
All 2 tests passed.
const std = @import("std");
const expect = std.testing.expect;
test"sentinel mismatch" {
var array = [_]u8{ 3, 2, 1, 0 };
// Creating a sentinel-terminated slice from the array with a length of 2// will result in the value `1` occupying the sentinel element position.// This does not match the indicated sentinel value of `0` and will lead// to a runtime panic.var runtime_length: usize = 2;
_ = &runtime_length;
const slice = array[0..runtime_length :0];
_ = slice;
}
Shell
$ zig test test_sentinel_mismatch.zig
1/1 test_sentinel_mismatch.test.sentinel mismatch...thread 201174 panic: sentinel mismatch: expected 0, found 1
/home/andy/dev/zig/doc/langref/test_sentinel_mismatch.zig:13:24: 0x1048a71 in test.sentinel mismatch (test)
const slice = array[0..runtime_length :0];
^/home/andy/dev/zig/lib/compiler/test_runner.zig:214:25: 0x10ef1d5 in mainTerminal (test)
if (test_fn.func()) |_| {
^/home/andy/dev/zig/lib/compiler/test_runner.zig:62:28: 0x10e75bd in main (test)
return mainTerminal();
^/home/andy/dev/zig/lib/std/start.zig:651:22: 0x10e6a32 in posixCallMainAndExit (test)
root.main();
^/home/andy/dev/zig/lib/std/start.zig:271:5: 0x10e660d in _start (test)
asm volatile (switch (native_arch) {
^???:?:?: 0x0 in ??? (???)
error: the following test command crashed:
/home/andy/dev/zig/.zig-cache/o/3ae1c7f3e7c9fd9d03520f85869dba54/test --seed=0xcab5d72d
// Declare a struct.// Zig gives no guarantees about the order of fields and the size of// the struct but the fields are guaranteed to be ABI-aligned.const Point = struct {
x: f32,
y: f32,
};
// Declare an instance of a struct.const p: Point = .{
.x = 0.12,
.y = 0.34,
};
// Functions in the struct's namespace can be called with dot syntax.const Vec3 = struct {
x: f32,
y: f32,
z: f32,
pubfninit(x: f32, y: f32, z: f32) Vec3 {
return Vec3{
.x = x,
.y = y,
.z = z,
};
}
pubfndot(self: Vec3, other: Vec3) f32 {
return self.x * other.x + self.y * other.y + self.z * other.z;
}
};
test"dot product" {
const v1 = Vec3.init(1.0, 0.0, 0.0);
const v2 = Vec3.init(0.0, 1.0, 0.0);
try expect(v1.dot(v2) == 0.0);
// Other than being available to call with dot syntax, struct methods are// not special. You can reference them as any other declaration inside// the struct:try expect(Vec3.dot(v1, v2) == 0.0);
}
// Structs can have declarations.// Structs can have 0 fields.const Empty = struct {
pubconst PI = 3.14;
};
test"struct namespaced variable" {
try expect(Empty.PI == 3.14);
try expect(@sizeOf(Empty) == 0);
// Empty structs can be instantiated the same as usual.const does_nothing: Empty = .{};
_ = does_nothing;
}
// Struct field order is determined by the compiler, however, a base pointer// can be computed from a field pointer:fnsetYBasedOnX(x: *f32, y: f32) void {
const point: *Point = @fieldParentPtr("x", x);
point.y = y;
}
test"field parent pointer" {
var point = Point{
.x = 0.1234,
.y = 0.5678,
};
setYBasedOnX(&point.x, 0.9);
try expect(point.y == 0.9);
}
// Structs can be returned from functions.fnLinkedList(comptime T: type) type {
returnstruct {
pubconst Node = struct {
prev: ?*Node,
next: ?*Node,
data: T,
};
first: ?*Node,
last: ?*Node,
len: usize,
};
}
test"linked list" {
// Functions called at compile-time are memoized.try expect(LinkedList(i32) == LinkedList(i32));
const list = LinkedList(i32){
.first = null,
.last = null,
.len = 0,
};
try expect(list.len == 0);
// Since types are first class values you can instantiate the type// by assigning it to a variable:const ListOfInts = LinkedList(i32);
try expect(ListOfInts == LinkedList(i32));
var node = ListOfInts.Node{
.prev = null,
.next = null,
.data = 1234,
};
const list2 = LinkedList(i32){
.first = &node,
.last = &node,
.len = 1,
};
// When using a pointer to a struct, fields can be accessed directly,// without explicitly dereferencing the pointer.// So you can dotry expect(list2.first.?.data == 1234);
// instead of try expect(list2.first.?.*.data == 1234);
}
const expect = @import("std").testing.expect;
$ zig test test_missized_packed_struct.zig/home/andy/dev/zig/doc/langref/test_missized_packed_struct.zig:2:29: error: backing integer type 'u32' has bit size 32 but the struct fields have a total bit size of 24
const S = packed struct(u32) { a: u16, b: u8 };
^~~
referenced by:
test.missized packed struct: /home/andy/dev/zig/doc/langref/test_missized_packed_struct.zig:2:22
$ zig test test_pointer_to_non-byte_aligned_field.zig
1/1 test_pointer_to_non-byte_aligned_field.test.pointer to non-byte-aligned field...OK
All 1 tests passed.
$ zig test test_packed_struct_field_address.zig
1/1 test_packed_struct_field_address.test.pointers of sub-byte-aligned fields share addresses...OK
All 1 tests passed.
const expect = @import("std").testing.expect;
const mem = @import("std").mem;
// Declare an enum.const Type = enum {
ok,
not_ok,
};
// Declare a specific enum field.const c = Type.ok;
// If you want access to the ordinal value of an enum, you// can specify the tag type.const Value = enum(u2) {
zero,
one,
two,
};
// Now you can cast between u2 and Value.// The ordinal value starts from 0, counting up by 1 from the previous member.test"enum ordinal value" {
try expect(@intFromEnum(Value.zero) == 0);
try expect(@intFromEnum(Value.one) == 1);
try expect(@intFromEnum(Value.two) == 2);
}
// You can override the ordinal value for an enum.const Value2 = enum(u32) {
hundred = 100,
thousand = 1000,
million = 1000000,
};
test"set enum ordinal value" {
try expect(@intFromEnum(Value2.hundred) == 100);
try expect(@intFromEnum(Value2.thousand) == 1000);
try expect(@intFromEnum(Value2.million) == 1000000);
}
// You can also override only some values.const Value3 = enum(u4) {
a,
b = 8,
c,
d = 4,
e,
};
test"enum implicit ordinal values and overridden values" {
try expect(@intFromEnum(Value3.a) == 0);
try expect(@intFromEnum(Value3.b) == 8);
try expect(@intFromEnum(Value3.c) == 9);
try expect(@intFromEnum(Value3.d) == 4);
try expect(@intFromEnum(Value3.e) == 5);
}
// Enums can have methods, the same as structs and unions.// Enum methods are not special, they are only namespaced// functions that you can call with dot syntax.const Suit = enum {
clubs,
spades,
diamonds,
hearts,
pubfnisClubs(self: Suit) bool {
return self == Suit.clubs;
}
};
test"enum method" {
const p = Suit.spades;
try expect(!p.isClubs());
}
// An enum can be switched upon.const Foo = enum {
string,
number,
none,
};
test"enum switch" {
const p = Foo.number;
const what_is_it = switch (p) {
Foo.string => "this is a string",
Foo.number => "this is a number",
Foo.none => "this is a none",
};
try expect(mem.eql(u8, what_is_it, "this is a number"));
}
// @typeInfo can be used to access the integer tag type of an enum.const Small = enum {
one,
two,
three,
four,
};
test"std.meta.Tag" {
try expect(@typeInfo(Small).@"enum".tag_type == u2);
}
// @typeInfo tells us the field count and the fields names:test"@typeInfo" {
try expect(@typeInfo(Small).@"enum".fields.len == 4);
try expect(mem.eql(u8, @typeInfo(Small).@"enum".fields[1].name, "two"));
}
// @tagName gives a [:0]const u8 representation of an enum value:test"@tagName" {
try expect(mem.eql(u8, @tagName(Small.three), "three"));
}
const Foo = enum { a, b, c };
exportfnentry(foo: Foo) void {
_ = foo;
}
Shell
$ zig build-obj enum_export_error.zig -target x86_64-linux/home/andy/dev/zig/doc/langref/enum_export_error.zig:2:17: error: parameter of type 'enum_export_error.Foo' not allowed in function with calling convention 'x86_64_sysv'
export fn entry(foo: Foo) void {
^~~~~~~~
/home/andy/dev/zig/doc/langref/enum_export_error.zig:2:17: note: enum tag type 'u2' is not extern compatible
/home/andy/dev/zig/doc/langref/enum_export_error.zig:2:17: note: only integers with 0, 8, 16, 32, 64 and 128 bits are extern compatible
/home/andy/dev/zig/doc/langref/enum_export_error.zig:1:13: note: enum declared here
const Foo = enum { a, b, c };
^~~~~~~~~~~~~~~~
referenced by:
root: /home/andy/dev/zig/lib/std/start.zig:3:22
comptime: /home/andy/dev/zig/lib/std/start.zig:27:9
2 reference(s) hidden; use '-freference-trace=4' to see all references
对于 C ABI 兼容的枚举,请为枚举提供显式标签类型。
enum_export.zig
const Foo = enum(c_int) { a, b, c };
exportfnentry(foo: Foo) void {
_ = foo;
}
$ zig test test_wrong_union_access.zig
1/1 test_wrong_union_access.test.simple union...thread 210915 panic: access of union field 'float' while field 'int' is active
/home/andy/dev/zig/doc/langref/test_wrong_union_access.zig:8:12: 0x1048a5f in test.simple union (test)
payload.float = 12.34;
^/home/andy/dev/zig/lib/compiler/test_runner.zig:214:25: 0x10ef2e5 in mainTerminal (test)
if (test_fn.func()) |_| {
^/home/andy/dev/zig/lib/compiler/test_runner.zig:62:28: 0x10e76cd in main (test)
return mainTerminal();
^/home/andy/dev/zig/lib/std/start.zig:651:22: 0x10e6b42 in posixCallMainAndExit (test)
root.main();
^/home/andy/dev/zig/lib/std/start.zig:271:5: 0x10e671d in _start (test)
asm volatile (switch (native_arch) {
^???:?:?: 0x0 in ??? (???)
error: the following test command crashed:
/home/andy/dev/zig/.zig-cache/o/0e2ad1837ee3fe26786cd418343cba9e/test --seed=0xdea5f103
$ zig test test_tagged_union.zig
1/2 test_tagged_union.test.switch on tagged union...OK
2/2 test_tagged_union.test.get tag type...OK
All 2 tests passed.
const pi = 3.14;
test"inside test block" {
// Let's even go inside another block
{
var pi: i32 = 1234;
}
}
Shell
$ zig test test_shadowing.zig/home/andy/dev/zig/doc/langref/test_shadowing.zig:6:13: error: local variable shadows declaration of 'pi'
var pi: i32 = 1234;
^~
/home/andy/dev/zig/doc/langref/test_shadowing.zig:1:1: note: declared here
const pi = 3.14;
^~~~~~~~~~~~~~~
const std = @import("std");
const builtin = @import("builtin");
const expect = std.testing.expect;
test"switch simple" {
const a: u64 = 10;
const zz: u64 = 103;
// All branches of a switch expression must be able to be coerced to a// common type.//// Branches cannot fallthrough. If fallthrough behavior is desired, combine// the cases and use an if.const b = switch (a) {
// Multiple cases can be combined via a ','1, 2, 3 => 0,
// Ranges can be specified using the ... syntax. These are inclusive// of both ends.5...100 => 1,
// Branches can be arbitrarily complex.101 => blk: {
const c: u64 = 5;
break :blk c * 2 + 1;
},
// Switching on arbitrary expressions is allowed as long as the// expression is known at compile-time.
zz => zz,
blk: {
const d: u32 = 5;
const e: u32 = 100;
break :blk d + e;
} => 107,
// The else branch catches everything not already captured.// Else branches are mandatory unless the entire range of values// is handled.else => 9,
};
try expect(b == 1);
}
// Switch expressions can be used outside a function:const os_msg = switch (builtin.target.os.tag) {
.linux => "we found a linux user",
else => "not a linux user",
};
// Inside a function, switch statements implicitly are compile-time// evaluated if the target expression is compile-time known.test"switch inside function" {
switch (builtin.target.os.tag) {
.fuchsia => {
// On an OS other than fuchsia, block is not even analyzed,// so this compile error is not triggered.// On fuchsia this compile error would be triggered.@compileError("fuchsia not supported");
},
else => {},
}
}
Shell
$ zig test test_switch.zig
1/2 test_switch.test.switch simple...OK
2/2 test_switch.test.switch inside function...OK
All 2 tests passed.
const expect = @import("std").testing.expect;
test"switch on tagged union" {
const Point = struct {
x: u8,
y: u8,
};
const Item = union(enum) {
a: u32,
c: Point,
d,
e: u32,
};
var a = Item{ .c = Point{ .x = 1, .y = 2 } };
// Switching on more complex enums is allowed.const b = switch (a) {
// A capture group is allowed on a match, and will return the enum// value matched. If the payload types of both cases are the same// they can be put into the same switch prong.
Item.a, Item.e => |item| item,
// A reference to the matched value can be obtained using `*` syntax.
Item.c => |*item| blk: {
item.*.x += 1;
break :blk 6;
},
// No else is required if the types cases was exhaustively handled
Item.d => 8,
};
try expect(b == 6);
try expect(a.c.x == 2);
}
Shell
$ zig test test_switch_tagged_union.zig
1/1 test_switch_tagged_union.test.switch on tagged union...OK
All 1 tests passed.
const std = @import("std");
const expect = std.testing.expect;
const SliceTypeA = externstruct {
len: usize,
ptr: [*]u32,
};
const SliceTypeB = externstruct {
ptr: [*]SliceTypeA,
len: usize,
};
const AnySlice = union(enum) {
a: SliceTypeA,
b: SliceTypeB,
c: []constu8,
d: []AnySlice,
};
fnwithFor(any: AnySlice) usize {
const Tag = @typeInfo(AnySlice).@"union".tag_type.?;
inlinefor (@typeInfo(Tag).@"enum".fields) |field| {
// With `inline for` the function gets generated as// a series of `if` statements relying on the optimizer// to convert it to a switch.if (field.value == @intFromEnum(any)) {
return@field(any, field.name).len;
}
}
// When using `inline for` the compiler doesn't know that every// possible case has been handled requiring an explicit `unreachable`.unreachable;
}
fnwithSwitch(any: AnySlice) usize {
returnswitch (any) {
// With `inline else` the function is explicitly generated// as the desired switch and the compiler can check that// every possible case is handled.inlineelse => |slice| slice.len,
};
}
test"inline for and inline else similarity" {
const any = AnySlice{ .c = "hello" };
try expect(withFor(any) == 5);
try expect(withSwitch(any) == 5);
}
Shell
$ zig test test_inline_else.zig
1/1 test_inline_else.test.inline for and inline else similarity...OK
All 1 tests passed.
当对联合体使用内联分支进行切换时,可以使用额外的捕获来获取联合体的枚举标签值。
test_inline_switch_union_tag.zig
const std = @import("std");
const expect = std.testing.expect;
const U = union(enum) {
a: u32,
b: f32,
};
fngetNum(u: U) u32 {
switch (u) {
// Here `num` is a runtime-known value that is either// `u.a` or `u.b` and `tag` is `u`'s comptime-known tag value.inlineelse => |num, tag| {
if (tag == .b) {
return@intFromFloat(num);
}
return num;
},
}
}
test"test" {
const u = U{ .b = 42 };
try expect(getNum(u) == 42);
}
Shell
$ zig test test_inline_switch_union_tag.zig
1/1 test_inline_switch_union_tag.test.test...OK
All 1 tests passed.
const expect = @import("std").testing.expect;
test"for basics" {
const items = [_]i32{ 4, 5, 3, 4, 0 };
var sum: i32 = 0;
// For loops iterate over slices and arrays.for (items) |value| {
// Break and continue are supported.if (value == 0) {
continue;
}
sum += value;
}
try expect(sum == 16);
// To iterate over a portion of a slice, reslice.for (items[0..1]) |value| {
sum += value;
}
try expect(sum == 20);
// To access the index of iteration, specify a second condition as well// as a second capture value.var sum2: i32 = 0;
for (items, 0..) |_, i| {
try expect(@TypeOf(i) == usize);
sum2 += @as(i32, @intCast(i));
}
try expect(sum2 == 10);
// To iterate over consecutive integers, use the range syntax.// Unbounded range is always a compile error.var sum3: usize = 0;
for (0..5) |i| {
sum3 += i;
}
try expect(sum3 == 10);
}
test"multi object for" {
const items = [_]usize{ 1, 2, 3 };
const items2 = [_]usize{ 4, 5, 6 };
var count: usize = 0;
// Iterate over multiple objects.// All lengths must be equal at the start of the loop, otherwise detectable// illegal behavior occurs.for (items, items2) |i, j| {
count += i + j;
}
try expect(count == 21);
}
test"for reference" {
var items = [_]i32{ 3, 4, 2 };
// Iterate over the slice by reference by// specifying that the capture value is a pointer.for (&items) |*value| {
value.* += 1;
}
try expect(items[0] == 4);
try expect(items[1] == 5);
try expect(items[2] == 3);
}
test"for else" {
// For allows an else attached to it, the same as a while loop.const items = [_]?i32{ 3, 4, null, 5 };
// For loops can also be used as expressions.// Similar to while loops, when you break from a for loop, the else branch is not evaluated.var sum: i32 = 0;
const result = for (items) |value| {
if (value != null) {
sum += value.?;
}
} else blk: {
try expect(sum == 12);
break :blk sum;
};
try expect(result == 12);
}
const expect = @import("std").testing.expect;
test"if optional" {
// If expressions test for null.const a: ?u32 = 0;
if (a) |value| {
try expect(value == 0);
} else {
unreachable;
}
const b: ?u32 = null;
if (b) |_| {
unreachable;
} else {
try expect(true);
}
// The else is not required.if (a) |value| {
try expect(value == 0);
}
// To test against null only, use the binary equality operator.if (b == null) {
try expect(true);
}
// Access the value by reference using a pointer capture.var c: ?u32 = 3;
if (c) |*value| {
value.* = 2;
}
if (c) |value| {
try expect(value == 2);
} else {
unreachable;
}
}
test"if error union with optional" {
// If expressions test for errors before unwrapping optionals.// The |optional_value| capture's type is ?u32.const a: anyerror!?u32 = 0;
if (a) |optional_value| {
try expect(optional_value.? == 0);
} else |err| {
_ = err;
unreachable;
}
const b: anyerror!?u32 = null;
if (b) |optional_value| {
try expect(optional_value == null);
} else |_| {
unreachable;
}
const c: anyerror!?u32 = error.BadValue;
if (c) |optional_value| {
_ = optional_value;
unreachable;
} else |err| {
try expect(err == error.BadValue);
}
// Access the value by reference by using a pointer capture each time.var d: anyerror!?u32 = 3;
if (d) |*optional_value| {
if (optional_value.*) |*value| {
value.* = 9;
}
} else |_| {
unreachable;
}
if (d) |optional_value| {
try expect(optional_value.? == 9);
} else |_| {
unreachable;
}
}
Shell
$ zig test test_if_optionals.zig
1/2 test_if_optionals.test.if optional...OK
2/2 test_if_optionals.test.if error union with optional...OK
All 2 tests passed.
// unreachable is used to assert that control flow will never reach a// particular location:test"basic math" {
const x = 1;
const y = 2;
if (x + y != 3) {
unreachable;
}
}
Shell
$ zig test test_unreachable.zig
1/1 test_unreachable.test.basic math...OK
All 1 tests passed.
事实上,std.debug.assert就是这样实现的。
test_assertion_failure.zig
// This is how std.debug.assert is implementedfnassert(ok: bool) void {
if (!ok) unreachable; // assertion failure
}
// This test will fail because we hit unreachable.test"this will fail" {
assert(false);
}
Shell
$ zig test test_assertion_failure.zig
1/1 test_assertion_failure.test.this will fail...thread 209597 panic: reached unreachable code
/home/andy/dev/zig/doc/langref/test_assertion_failure.zig:3:14: 0x104892d in assert (test)
if (!ok) unreachable; // assertion failure
^/home/andy/dev/zig/doc/langref/test_assertion_failure.zig:8:11: 0x10488fa in test.this will fail (test)
assert(false);
^/home/andy/dev/zig/lib/compiler/test_runner.zig:214:25: 0x10ef0c5 in mainTerminal (test)
if (test_fn.func()) |_| {
^/home/andy/dev/zig/lib/compiler/test_runner.zig:62:28: 0x10e74ad in main (test)
return mainTerminal();
^/home/andy/dev/zig/lib/std/start.zig:651:22: 0x10e6922 in posixCallMainAndExit (test)
root.main();
^/home/andy/dev/zig/lib/std/start.zig:271:5: 0x10e64fd in _start (test)
asm volatile (switch (native_arch) {
^???:?:?: 0x0 in ??? (???)
error: the following test command crashed:
/home/andy/dev/zig/.zig-cache/o/0594f73810636b148c5e4293efd1faf6/test --seed=0x2a37766c
const assert = @import("std").debug.assert;
test"type of unreachable" {
comptime {
// The type of unreachable is noreturn.// However this assertion will still fail to compile because// unreachable expressions are compile errors.
assert(@TypeOf(unreachable) == noreturn);
}
}
Shell
$ zig test test_comptime_unreachable.zig/home/andy/dev/zig/doc/langref/test_comptime_unreachable.zig:10:16: error: unreachable code
assert(@TypeOf(unreachable) == noreturn);
^~~~~~~~~~~~~~~~~~~~
/home/andy/dev/zig/doc/langref/test_comptime_unreachable.zig:10:24: note: control flow is diverted here
assert(@TypeOf(unreachable) == noreturn);
^~~~~~~~~~~
const std = @import("std");
const builtin = @import("builtin");
const native_arch = builtin.cpu.arch;
const expect = std.testing.expect;
// Functions are declared like thisfnadd(a: i8, b: i8) i8 {
if (a == 0) {
return b;
}
return a + b;
}
// The export specifier makes a function externally visible in the generated// object file, and makes it use the C ABI.exportfnsub(a: i8, b: i8) i8 {
return a - b;
}
// The extern specifier is used to declare a function that will be resolved// at link time, when linking statically, or at runtime, when linking// dynamically. The quoted identifier after the extern keyword specifies// the library that has the function. (e.g. "c" -> libc.so)// The callconv specifier changes the calling convention of the function.const WINAPI: std.builtin.CallingConvention = if (native_arch == .x86) .Stdcall else .C;
extern"kernel32"fnExitProcess(exit_code: u32) callconv(WINAPI) noreturn;
extern"c"fnatan2(a: f64, b: f64) f64;
// The @branchHint builtin can be used to tell the optimizer that a function is rarely called ("cold").fnabort() noreturn {
@branchHint(.cold);
while (true) {}
}
// The naked calling convention makes a function not have any function prologue or epilogue.// This can be useful when integrating with assembly.fn_start() callconv(.Naked) noreturn {
abort();
}
// The inline calling convention forces a function to be inlined at all call sites.// If the function cannot be inlined, it is a compile-time error.inlinefnshiftLeftOne(a: u32) u32 {
return a << 1;
}
// The pub specifier allows the function to be visible when importing.// Another file can use @import and call sub2pubfnsub2(a: i8, b: i8) i8 {
return a - b;
}
// Function pointers are prefixed with `*const `.const Call2Op = *constfn (a: i8, b: i8) i8;
fndoOp(fnCall: Call2Op, op1: i8, op2: i8) i8 {
return fnCall(op1, op2);
}
test"function" {
try expect(doOp(add, 5, 6) == 11);
try expect(doOp(sub2, 5, 6) == -1);
}
Shell
$ zig test test_functions.zig
1/1 test_functions.test.function...OK
All 1 tests passed.
const Point = struct {
x: i32,
y: i32,
};
fnfoo(point: Point) i32 {
// Here, `point` could be a reference, or a copy. The function body// can ignore the difference and treat it as a value. Be very careful// taking the address of the parameter - it should be treated as if// the address will become invalid when the function returns.return point.x + point.y;
}
const expect = @import("std").testing.expect;
test"pass struct to function" {
try expect(foo(Point{ .x = 1, .y = 2 }) == 3);
}
Shell
$ zig test test_pass_by_reference_or_value.zig
1/1 test_pass_by_reference_or_value.test.pass struct to function...OK
All 1 tests passed.
$ zig test test_coerce_error_superset_to_subset.zig/home/andy/dev/zig/doc/langref/test_coerce_error_superset_to_subset.zig:16:12: error: expected type 'error{OutOfMemory}', found 'error{AccessDenied,OutOfMemory,FileNotFound}'
return err;
^~~
/home/andy/dev/zig/doc/langref/test_coerce_error_superset_to_subset.zig:16:12: note: 'error.AccessDenied' not a member of destination error set
/home/andy/dev/zig/doc/langref/test_coerce_error_superset_to_subset.zig:16:12: note: 'error.FileNotFound' not a member of destination error set
/home/andy/dev/zig/doc/langref/test_coerce_error_superset_to_subset.zig:15:28: note: function return type declared here
fn foo(err: FileOpenError) AllocationError {
^~~~~~~~~~~~~~~
referenced by:
test.coerce superset to subset: /home/andy/dev/zig/doc/langref/test_coerce_error_superset_to_subset.zig:12:8
fncreateFoo(param: i32) !Foo {
const foo = try tryToAllocateFoo();
// now we have allocated foo. we need to free it if the function fails.// but we want to return it if the function succeeds.errdefer deallocateFoo(foo);
const tmp_buf = allocateTmpBuffer() orelsereturnerror.OutOfMemory;
// tmp_buf is truly a temporary resource, and we for sure want to clean it up// before this block leaves scopedefer deallocateTmpBuffer(tmp_buf);
if (param > 1337) returnerror.InvalidParam;
// here the errdefer will not run since we're returning success from the function.// but the defer will run!return foo;
}
const expect = @import("std").testing.expect;
test"error union" {
var foo: anyerror!i32 = undefined;
// Coerce from child type of an error union:
foo = 1234;
// Coerce from an error set:
foo = error.SomeError;
// Use compile-time reflection to access the payload type of an error union:trycomptime expect(@typeInfo(@TypeOf(foo)).error_union.payload == i32);
// Use compile-time reflection to access the error set type of an error union:trycomptime expect(@typeInfo(@TypeOf(foo)).error_union.error_set == anyerror);
}
Shell
$ zig test test_error_union.zig
1/1 test_error_union.test.error union...OK
All 1 tests passed.
const expect = @import("std").testing.expect;
test"optional type" {
// Declare an optional and coerce from null:var foo: ?i32 = null;
// Coerce from child type of an optional
foo = 1234;
// Use compile-time reflection to access the child type of the optional:trycomptime expect(@typeInfo(@TypeOf(foo)).optional.child == i32);
}
Shell
$ zig test test_optional_type.zig
1/1 test_optional_type.test.optional type...OK
All 1 tests passed.
const expect = @import("std").testing.expect;
test"optional pointers" {
// Pointers cannot be null. If you want a null pointer, use the optional// prefix `?` to make the pointer type optional.var ptr: ?*i32 = null;
var x: i32 = 1;
ptr = &x;
try expect(ptr.?.* == 1);
// Optional pointers are the same size as normal pointers, because pointer// value 0 is used as the null value.try expect(@sizeOf(?*i32) == @sizeOf(*i32));
}
Shell
$ zig test test_optional_pointer.zig
1/1 test_optional_pointer.test.optional pointers...OK
All 1 tests passed.
const std = @import("std");
const expect = std.testing.expect;
// You can assign constant pointers to arrays to a slice with// const modifier on the element type. Useful in particular for// String literals.test"*const [N]T to []const T" {
const x1: []constu8 = "hello";
const x2: []constu8 = &[5]u8{ 'h', 'e', 'l', 'l', 111 };
try expect(std.mem.eql(u8, x1, x2));
const y: []constf32 = &[2]f32{ 1.2, 3.4 };
try expect(y[0] == 1.2);
}
// Likewise, it works when the destination type is an error union.test"*const [N]T to E![]const T" {
const x1: anyerror![]constu8 = "hello";
const x2: anyerror![]constu8 = &[5]u8{ 'h', 'e', 'l', 'l', 111 };
try expect(std.mem.eql(u8, try x1, try x2));
const y: anyerror![]constf32 = &[2]f32{ 1.2, 3.4 };
try expect((try y)[0] == 1.2);
}
// Likewise, it works when the destination type is an optional.test"*const [N]T to ?[]const T" {
const x1: ?[]constu8 = "hello";
const x2: ?[]constu8 = &[5]u8{ 'h', 'e', 'l', 'l', 111 };
try expect(std.mem.eql(u8, x1.?, x2.?));
const y: ?[]constf32 = &[2]f32{ 1.2, 3.4 };
try expect(y.?[0] == 1.2);
}
// In this cast, the array length becomes the slice length.test"*[N]T to []T" {
var buf: [5]u8 = "hello".*;
const x: []u8 = &buf;
try expect(std.mem.eql(u8, x, "hello"));
const buf2 = [2]f32{ 1.2, 3.4 };
const x2: []constf32 = &buf2;
try expect(std.mem.eql(f32, x2, &[2]f32{ 1.2, 3.4 }));
}
// Single-item pointers to arrays can be coerced to many-item pointers.test"*[N]T to [*]T" {
var buf: [5]u8 = "hello".*;
const x: [*]u8 = &buf;
try expect(x[4] == 'o');
// x[5] would be an uncaught out of bounds pointer dereference!
}
// Likewise, it works when the destination type is an optional.test"*[N]T to ?[*]T" {
var buf: [5]u8 = "hello".*;
const x: ?[*]u8 = &buf;
try expect(x.?[4] == 'o');
}
// Single-item pointers can be cast to len-1 single-item arrays.test"*T to *[1]T" {
var x: i32 = 1234;
const y: *[1]i32 = &x;
const z: [*]i32 = y;
try expect(z[0] == 1234);
}
Shell
$ zig test test_coerce_slices_arrays_and_pointers.zig
1/7 test_coerce_slices_arrays_and_pointers.test.*const [N]T to []const T...OK
2/7 test_coerce_slices_arrays_and_pointers.test.*const [N]T to E![]const T...OK
3/7 test_coerce_slices_arrays_and_pointers.test.*const [N]T to ?[]const T...OK
4/7 test_coerce_slices_arrays_and_pointers.test.*[N]T to []T...OK
5/7 test_coerce_slices_arrays_and_pointers.test.*[N]T to [*]T...OK
6/7 test_coerce_slices_arrays_and_pointers.test.*[N]T to ?[*]T...OK
7/7 test_coerce_slices_arrays_and_pointers.test.*T to *[1]T...OK
All 7 tests passed.
$ zig test test_coerce_optional_wrapped_error_union.zig
1/1 test_coerce_optional_wrapped_error_union.test.coerce to optionals wrapped in error union...OK
All 1 tests passed.
const std = @import("std");
const expect = std.testing.expect;
test"coercing large integer type to smaller one when value is comptime-known to fit" {
const x: u64 = 255;
const y: u8 = x;
try expect(y == 255);
}
Shell
$ zig test test_coerce_large_to_small.zig
1/1 test_coerce_large_to_small.test.coercing large integer type to smaller one when value is comptime-known to fit...OK
All 1 tests passed.
const std = @import("std");
const expect = std.testing.expect;
const mem = std.mem;
test"peer resolve int widening" {
const a: i8 = 12;
const b: i16 = 34;
const c = a + b;
try expect(c == 46);
try expect(@TypeOf(c) == i16);
}
test"peer resolve arrays of different size to const slice" {
try expect(mem.eql(u8, boolToStr(true), "true"));
try expect(mem.eql(u8, boolToStr(false), "false"));
trycomptime expect(mem.eql(u8, boolToStr(true), "true"));
trycomptime expect(mem.eql(u8, boolToStr(false), "false"));
}
fnboolToStr(b: bool) []constu8 {
returnif (b) "true"else"false";
}
test"peer resolve array and const slice" {
try testPeerResolveArrayConstSlice(true);
trycomptime testPeerResolveArrayConstSlice(true);
}
fntestPeerResolveArrayConstSlice(b: bool) !void {
const value1 = if (b) "aoeu"else@as([]constu8, "zz");
const value2 = if (b) @as([]constu8, "zz") else"aoeu";
try expect(mem.eql(u8, value1, "aoeu"));
try expect(mem.eql(u8, value2, "zz"));
}
test"peer type resolution: ?T and T" {
try expect(peerTypeTAndOptionalT(true, false).? == 0);
try expect(peerTypeTAndOptionalT(false, false).? == 3);
comptime {
try expect(peerTypeTAndOptionalT(true, false).? == 0);
try expect(peerTypeTAndOptionalT(false, false).? == 3);
}
}
fnpeerTypeTAndOptionalT(c: bool, b: bool) ?usize {
if (c) {
returnif (b) nullelse@as(usize, 0);
}
return@as(usize, 3);
}
test"peer type resolution: *[0]u8 and []const u8" {
try expect(peerTypeEmptyArrayAndSlice(true, "hi").len == 0);
try expect(peerTypeEmptyArrayAndSlice(false, "hi").len == 1);
comptime {
try expect(peerTypeEmptyArrayAndSlice(true, "hi").len == 0);
try expect(peerTypeEmptyArrayAndSlice(false, "hi").len == 1);
}
}
fnpeerTypeEmptyArrayAndSlice(a: bool, slice: []constu8) []constu8 {
if (a) {
return &[_]u8{};
}
return slice[0..1];
}
test"peer type resolution: *[0]u8, []const u8, and anyerror![]u8" {
{
var data = "hi".*;
const slice = data[0..];
try expect((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0);
try expect((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1);
}
comptime {
var data = "hi".*;
const slice = data[0..];
try expect((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0);
try expect((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1);
}
}
fnpeerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) anyerror![]u8 {
if (a) {
return &[_]u8{};
}
return slice[0..1];
}
test"peer type resolution: *const T and ?*T" {
const a: *constusize = @ptrFromInt(0x123456780);
const b: ?*usize = @ptrFromInt(0x123456780);
try expect(a == b);
try expect(b == a);
}
test"peer type resolution: error union switch" {
// The non-error and error cases are only peers if the error case is just a switch expression;// the pattern `if (x) {...} else |err| blk: { switch (err) {...} }` does not consider the// non-error and error case to be peers.var a: error{ A, B, C }!u32 = 0;
_ = &a;
const b = if (a) |x|
x + 3else |err| switch (err) {
error.A => 0,
error.B => 1,
error.C => null,
};
try expect(@TypeOf(b) == ?u32);
// The non-error and error cases are only peers if the error case is just a switch expression;// the pattern `x catch |err| blk: { switch (err) {...} }` does not consider the unwrapped `x`// and error case to be peers.const c = a catch |err| switch (err) {
error.A => 0,
error.B => 1,
error.C => null,
};
try expect(@TypeOf(c) == ?u32);
}
Shell
$ zig test test_peer_type_resolution.zig
1/8 test_peer_type_resolution.test.peer resolve int widening...OK
2/8 test_peer_type_resolution.test.peer resolve arrays of different size to const slice...OK
3/8 test_peer_type_resolution.test.peer resolve array and const slice...OK
4/8 test_peer_type_resolution.test.peer type resolution: ?T and T...OK
5/8 test_peer_type_resolution.test.peer type resolution: *[0]u8 and []const u8...OK
6/8 test_peer_type_resolution.test.peer type resolution: *[0]u8, []const u8, and anyerror![]u8...OK
7/8 test_peer_type_resolution.test.peer type resolution: *const T and ?*T...OK
8/8 test_peer_type_resolution.test.peer type resolution: error union switch...OK
All 8 tests passed.
$ zig test test_expression_ignored.zig/home/andy/dev/zig/doc/langref/test_expression_ignored.zig:2:8: error: value of type 'i32' ignored
foo();
~~~^~
/home/andy/dev/zig/doc/langref/test_expression_ignored.zig:2:8: note: all non-void values must be used
/home/andy/dev/zig/doc/langref/test_expression_ignored.zig:2:8: note: to discard the value, assign it to '_'
const expectEqual = @import("std").testing.expectEqual;
test"result type propagates through struct initializer" {
const S = struct { x: u32 };
const val: u64 = 123;
const s: S = .{ .x = @intCast(val) };
// .{ .x = @intCast(val) } has result type `S` due to the type annotation// @intCast(val) has result type `u32` due to the type of the field `S.x`// val has no result type, as it is permitted to be any integer typetry expectEqual(@as(u32, 123), s.x);
}
Shell
$ zig test result_type_propagation.zig
1/1 result_type_propagation.test.result type propagates through struct initializer...OK
All 1 tests passed.
const expect = @import("std").testing.expect;
test"attempt to swap array elements with array initializer" {
var arr: [2]u32 = .{ 1, 2 };
arr = .{ arr[1], arr[0] };
// The previous line is equivalent to the following two lines:// arr[0] = arr[1];// arr[1] = arr[0];// So this fails!try expect(arr[0] == 2); // succeedstry expect(arr[1] == 1); // fails
}
Shell
$ zig test result_location_interfering_with_swap.zig
1/1 result_location_interfering_with_swap.test.attempt to swap array elements with array initializer...FAIL (TestUnexpectedResult)
/home/andy/dev/zig/lib/std/testing.zig:580:14: 0x10488ef in expect (test)
if (!ok) return error.TestUnexpectedResult;
^/home/andy/dev/zig/doc/langref/result_location_interfering_with_swap.zig:10:5: 0x10489d5 in test.attempt to swap array elements with array initializer (test)
try expect(arr[1] == 1); // fails
^
0 passed; 0 skipped; 1 failed.
error: the following test command failed with exit code 1:
/home/andy/dev/zig/.zig-cache/o/ad052551079782d9997770925a86a745/test --seed=0x7bedf42
fnmax(comptime T: type, a: T, b: T) T {
returnif (a > b) a else b;
}
test"try to compare bools" {
_ = max(bool, true, false);
}
Shell
$ zig test test_comptime_mismatched_type.zig/home/andy/dev/zig/doc/langref/test_comptime_mismatched_type.zig:2:18: error: operator > not allowed for type 'bool'
return if (a > b) a else b;
~~^~~
referenced by:
test.try to compare bools: /home/andy/dev/zig/doc/langref/test_comptime_mismatched_type.zig:5:12
// From the line:// expect(performFn('t', 1) == 6);fnperformFn(start_value: i32) i32 {
var result: i32 = start_value;
result = two(result);
result = three(result);
return result;
}
performFn_2
// From the line:// expect(performFn('o', 0) == 1);fnperformFn(start_value: i32) i32 {
var result: i32 = start_value;
result = one(result);
return result;
}
performFn_3
// From the line:// expect(performFn('w', 99) == 99);fnperformFn(start_value: i32) i32 {
var result: i32 = start_value;
_ = &result;
return result;
}
$ zig test test_fibonacci_comptime_overflow.zig/home/andy/dev/zig/doc/langref/test_fibonacci_comptime_overflow.zig:5:28: error: overflow of integer type 'u32' with value '-1'
return fibonacci(index - 1) + fibonacci(index - 2);
~~~~~~^~~
/home/andy/dev/zig/doc/langref/test_fibonacci_comptime_overflow.zig:5:21: note: called from here (7 times)
return fibonacci(index - 1) + fibonacci(index - 2);
~~~~~~~~~^~~~~~~~~~~
/home/andy/dev/zig/doc/langref/test_fibonacci_comptime_overflow.zig:9:34: note: called from here
try comptime expect(fibonacci(7) == 13);
~~~~~~~~~^~~
fnList(comptime T: type) type {
returnstruct {
items: []T,
len: usize,
};
}
// The generic List data structure can be instantiated by passing in a type:var buffer: [10]i32 = undefined;
var list = List(i32){
.items = &buffer,
.len = 0,
};
const print = @import("std").debug.print;
const a_number: i32 = 1234;
const a_string = "foobar";
test"print too many arguments" {
print("here is a string: '{s}' here is a number: {}\n", .{
a_string,
a_number,
a_number,
});
}
Shell
$ zig test test_print_too_many_args.zig/home/andy/dev/zig/lib/std/fmt.zig:211:18: error: unused argument in 'here is a string: '{s}' here is a number: {}
'
1 => @compileError("unused argument in '" ++ fmt ++ "'"),
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
referenced by:
print__anon_680: /home/andy/dev/zig/lib/std/io/Writer.zig:24:26
print__anon_420: /home/andy/dev/zig/lib/std/io.zig:312:47
1 reference(s) hidden; use '-freference-trace=3' to see all references
$ zig build-exe inline_assembly.zig -target x86_64-linux
$ ./inline_assembly
hello world
解析语法
Assembly Syntax Explained.zig
pubfnsyscall1(number: usize, arg1: usize) usize {
// Inline assembly is an expression which returns a value.// the `asm` keyword begins the expression.returnasm// `volatile` is an optional modifier that tells Zig this// inline assembly expression has side-effects. Without// `volatile`, Zig is allowed to delete the inline assembly// code if the result is unused.volatile (
// Next is a comptime string which is the assembly code.// Inside this string one may use `%[ret]`, `%[number]`,// or `%[arg1]` where a register is expected, to specify// the register that Zig uses for the argument or return value,// if the register constraint strings are used. However in// the below code, this is not used. A literal `%` can be// obtained by escaping it with a double percent: `%%`.// Often multiline string syntax comes in handy here.\\syscall// Next is the output. It is possible in the future Zig will// support multiple outputs, depending on how// https://github.com/ziglang/zig/issues/215 is resolved.// It is allowed for there to be no outputs, in which case// this colon would be directly followed by the colon for the inputs.
:
// This specifies the name to be used in `%[ret]` syntax in// the above assembly string. This example does not use it,// but the syntax is mandatory.
[ret]
// Next is the output constraint string. This feature is still// considered unstable in Zig, and so LLVM/GCC documentation// must be used to understand the semantics.// http://releases.llvm.org/10.0.0/docs/LangRef.html#inline-asm-constraint-string// https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html// In this example, the constraint string means "the result value of// this inline assembly instruction is whatever is in $rax"."={rax}"// Next is either a value binding, or `->` and then a type. The// type is the result type of the inline assembly expression.// If it is a value binding, then `%[ret]` syntax would be used// to refer to the register bound to the value.
(-> usize),
// Next is the list of inputs.// The constraint for these inputs means, "when the assembly code is// executed, $rax shall have the value of `number` and $rdi shall have// the value of `arg1`". Any number of input parameters is allowed,// including none.
: [number] "{rax}" (number),
[arg1] "{rdi}" (arg1),
// Next is the list of clobbers. These declare a set of registers whose// values will not be preserved by the execution of this assembly code.// These do not include output or input registers. The special clobber// value of "memory" means that the assembly writes to arbitrary undeclared// memory locations - not only the memory pointed to by a declared indirect// output. In this example we list $rcx and $r11 because it is known the// kernel syscall does not preserve these registers.
: "rcx", "r11"
);
}
$ zig test test_call_builtin.zig
1/1 test_call_builtin.test.noinline function call...OK
All 1 tests passed.
@call 比普通的函数调用语法更灵活。CallModifier 枚举在此处重现
builtin.CallModifier struct.zig
pubconst CallModifier = enum {
/// Equivalent to function call syntax.
auto,
/// Equivalent to async keyword used with function call syntax.
async_kw,
/// Prevents tail call optimization. This guarantees that the return/// address will point to the callsite, as opposed to the callsite's/// callsite. If the call is otherwise required to be tail-called/// or inlined, a compile error is emitted instead.
never_tail,
/// Guarantees that the call will not be inlined. If the call is/// otherwise required to be inlined, a compile error is emitted instead.
never_inline,
/// Asserts that the function call will not suspend. This allows a/// non-async function to call an async function.
no_async,
/// Guarantees that the call will be generated with tail call optimization./// If this is not possible, a compile error is emitted instead.
always_tail,
/// Guarantees that the call will be inlined at the callsite./// If this is not possible, a compile error is emitted instead.
always_inline,
/// Evaluates the call at compile-time. If the call cannot be completed at/// compile-time, a compile error is emitted instead.
compile_time,
};
$ zig test test_field_builtin.zig
1/2 test_field_builtin.test.field access by string...OK
2/2 test_field_builtin.test.decl access by string...OK
All 2 tests passed.
const std = @import("std");
const expect = std.testing.expect;
const Foo = struct {
nope: i32,
pubvar blah = "xxx";
const hi = 1;
};
test"@hasDecl" {
try expect(@hasDecl(Foo, "blah"));
// Even though `hi` is private, @hasDecl returns true because this test is// in the same file scope as Foo. It would return false if Foo was declared// in a different file.try expect(@hasDecl(Foo, "hi"));
// @hasDecl is for declarations; not fields.try expect(!@hasDecl(Foo, "nope"));
try expect(!@hasDecl(Foo, "nope1234"));
}
Shell
$ zig test test_hasDecl_builtin.zig
1/1 test_hasDecl_builtin.test.@hasDecl...OK
All 1 tests passed.
test"foo" {
comptime {
var i = 0;
while (i < 1001) : (i += 1) {}
}
}
Shell
$ zig test test_without_setEvalBranchQuota_builtin.zig/home/andy/dev/zig/doc/langref/test_without_setEvalBranchQuota_builtin.zig:4:9: error: evaluation exceeded 1000 backwards branches
while (i < 1001) : (i += 1) {}
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/andy/dev/zig/doc/langref/test_without_setEvalBranchQuota_builtin.zig:4:9: note: use @setEvalBranchQuota() to raise the branch limit from 1000
现在我们使用 @setEvalBranchQuota
test_setEvalBranchQuota_builtin.zig
test"foo" {
comptime {
@setEvalBranchQuota(1001);
var i = 0;
while (i < 1001) : (i += 1) {}
}
}
Shell
$ zig test test_setEvalBranchQuota_builtin.zig
1/1 test_setEvalBranchQuota_builtin.test.foo...OK
All 1 tests passed.
test"@setRuntimeSafety" {
// The builtin applies to the scope that it is called in. So here, integer overflow// will not be caught in ReleaseFast and ReleaseSmall modes:// var x: u8 = 255;// x += 1; // Unchecked Illegal Behavior in ReleaseFast/ReleaseSmall modes.
{
// However this block has safety enabled, so safety checks happen here,// even in ReleaseFast and ReleaseSmall modes.@setRuntimeSafety(true);
var x: u8 = 255;
x += 1;
{
// The value can be overridden at any scope. So here integer overflow// would not be caught in any build mode.@setRuntimeSafety(false);
// var x: u8 = 255;// x += 1; // Unchecked Illegal Behavior in all build modes.
}
}
}
Shell
$ zig test test_setRuntimeSafety_builtin.zig -OReleaseFast
1/1 test_setRuntimeSafety_builtin.test.@setRuntimeSafety...thread 210982 panic: integer overflow
/home/andy/dev/zig/doc/langref/test_setRuntimeSafety_builtin.zig:11:11: 0x100b138 in test.@setRuntimeSafety (test)
x += 1;
^/home/andy/dev/zig/lib/compiler/test_runner.zig:214:25: 0x1033268 in main (test)
if (test_fn.func()) |_| {
^/home/andy/dev/zig/lib/std/start.zig:651:22: 0x10318fd in posixCallMainAndExit (test)
root.main();
^/home/andy/dev/zig/lib/std/start.zig:271:5: 0x103140d in _start (test)
asm volatile (switch (native_arch) {
^???:?:?: 0x0 in ??? (???)
error: the following test command crashed:
/home/andy/dev/zig/.zig-cache/o/5d6f3765262213cc9dd53d1c647e7bc5/test --seed=0xefe8119a
$ zig test test_comptime_reaching_unreachable.zig/home/andy/dev/zig/doc/langref/test_comptime_reaching_unreachable.zig:5:14: error: reached unreachable code
if (!ok) unreachable; // assertion failure
^~~~~~~~~~~
/home/andy/dev/zig/doc/langref/test_comptime_reaching_unreachable.zig:2:11: note: called from here
assert(false);
~~~~~~^~~~~~~
$ zig build-exe runtime_index_out_of_bounds.zig
$ ./runtime_index_out_of_bounds
thread 207520 panic: index out of bounds: index 5, len 5
/home/andy/dev/zig/doc/langref/runtime_index_out_of_bounds.zig:7:13: 0x10df121 in foo (runtime_index_out_of_bounds)
return x[5];
^/home/andy/dev/zig/doc/langref/runtime_index_out_of_bounds.zig:2:18: 0x10de886 in main (runtime_index_out_of_bounds)
const x = foo("hello");
^/home/andy/dev/zig/lib/std/start.zig:651:22: 0x10de272 in posixCallMainAndExit (runtime_index_out_of_bounds)
root.main();
^/home/andy/dev/zig/lib/std/start.zig:271:5: 0x10dde4d in _start (runtime_index_out_of_bounds)
asm volatile (switch (native_arch) {
^???:?:?: 0x0 in ??? (???)
(process terminated by signal)
$ zig test test_comptime_overflow.zig/home/andy/dev/zig/doc/langref/test_comptime_overflow.zig:3:10: error: overflow of integer type 'u8' with value '256'
byte += 1;
~~~~~^~~~
$ zig test test_comptime_division_by_zero.zig/home/andy/dev/zig/doc/langref/test_comptime_division_by_zero.zig:4:19: error: division by zero here causes undefined behavior
const c = a / b;
^
运行时
runtime_division_by_zero.zig
const std = @import("std");
pubfnmain() void {
var a: u32 = 1;
var b: u32 = 0;
_ = .{ &a, &b };
const c = a / b;
std.debug.print("value: {}\n", .{c});
}
Shell
$ zig build-exe runtime_division_by_zero.zig
$ ./runtime_division_by_zero
thread 210516 panic: division by zero
/home/andy/dev/zig/doc/langref/runtime_division_by_zero.zig:7:17: 0x10de9ca in main (runtime_division_by_zero)
const c = a / b;
^/home/andy/dev/zig/lib/std/start.zig:651:22: 0x10de372 in posixCallMainAndExit (runtime_division_by_zero)
root.main();
^/home/andy/dev/zig/lib/std/start.zig:271:5: 0x10ddf4d in _start (runtime_division_by_zero)
asm volatile (switch (native_arch) {
^???:?:?: 0x0 in ??? (???)
(process terminated by signal)
$ zig test test_comptime_remainder_division_by_zero.zig/home/andy/dev/zig/doc/langref/test_comptime_remainder_division_by_zero.zig:4:19: error: division by zero here causes undefined behavior
const c = a % b;
^
运行时
runtime_remainder_division_by_zero.zig
const std = @import("std");
pubfnmain() void {
var a: u32 = 10;
var b: u32 = 0;
_ = .{ &a, &b };
const c = a % b;
std.debug.print("value: {}\n", .{c});
}
Shell
$ zig build-exe runtime_remainder_division_by_zero.zig
$ ./runtime_remainder_division_by_zero
thread 204394 panic: division by zero
/home/andy/dev/zig/doc/langref/runtime_remainder_division_by_zero.zig:7:17: 0x10de9ca in main (runtime_remainder_division_by_zero)
const c = a % b;
^/home/andy/dev/zig/lib/std/start.zig:651:22: 0x10de372 in posixCallMainAndExit (runtime_remainder_division_by_zero)
root.main();
^/home/andy/dev/zig/lib/std/start.zig:271:5: 0x10ddf4d in _start (runtime_remainder_division_by_zero)
asm volatile (switch (native_arch) {
^???:?:?: 0x0 in ??? (???)
(process terminated by signal)
$ zig test test_comptime_divExact_remainder.zig/home/andy/dev/zig/doc/langref/test_comptime_divExact_remainder.zig:4:15: error: exact division produced remainder
const c = @divExact(a, b);
^~~~~~~~~~~~~~~
$ zig test test_comptime_unwrap_null.zig/home/andy/dev/zig/doc/langref/test_comptime_unwrap_null.zig:3:35: error: unable to unwrap null
const number = optional_number.?;
~~~~~~~~~~~~~~~^~
$ zig build-exe runtime_unwrap_null.zig
$ ./runtime_unwrap_null
thread 210622 panic: attempt to use null value
/home/andy/dev/zig/doc/langref/runtime_unwrap_null.zig:6:35: 0x10de9b6 in main (runtime_unwrap_null)
const number = optional_number.?;
^/home/andy/dev/zig/lib/std/start.zig:651:22: 0x10de372 in posixCallMainAndExit (runtime_unwrap_null)
root.main();
^/home/andy/dev/zig/lib/std/start.zig:271:5: 0x10ddf4d in _start (runtime_unwrap_null)
asm volatile (switch (native_arch) {
^???:?:?: 0x0 in ??? (???)
(process terminated by signal)
$ zig test test_comptime_invalid_enum_cast.zig/home/andy/dev/zig/doc/langref/test_comptime_invalid_enum_cast.zig:8:20: error: enum 'test_comptime_invalid_enum_cast.Foo' has no tag with value '3'
const b: Foo = @enumFromInt(a);
^~~~~~~~~~~~~~~
/home/andy/dev/zig/doc/langref/test_comptime_invalid_enum_cast.zig:1:13: note: enum declared here
const Foo = enum {
^~~~
$ zig test test_comptime_invalid_error_set_cast.zig/home/andy/dev/zig/doc/langref/test_comptime_invalid_error_set_cast.zig:10:19: error: 'error.B' not a member of error set 'error{A,C}'
_ = @as(Set2, @errorCast(Set1.B));
^~~~~~~~~~~~~~~~~~
comptime {
var f = Foo{ .int = 42 };
f.float = 12.34;
}
const Foo = union {
float: f32,
int: u32,
};
Shell
$ zig test test_comptime_wrong_union_field_access.zig/home/andy/dev/zig/doc/langref/test_comptime_wrong_union_field_access.zig:3:6: error: access of union field 'float' while field 'int' is active
f.float = 12.34;
~^~~~~~
/home/andy/dev/zig/doc/langref/test_comptime_wrong_union_field_access.zig:6:13: note: union declared here
const Foo = union {
^~~~~
$ zig build-exe runtime_wrong_union_field_access.zig
$ ./runtime_wrong_union_field_access
thread 202573 panic: access of union field 'float' while field 'int' is active
/home/andy/dev/zig/doc/langref/runtime_wrong_union_field_access.zig:14:6: 0x10e4b18 in bar (runtime_wrong_union_field_access)
f.float = 12.34;
^/home/andy/dev/zig/doc/langref/runtime_wrong_union_field_access.zig:10:8: 0x10e426c in main (runtime_wrong_union_field_access)
bar(&f);
^/home/andy/dev/zig/lib/std/start.zig:651:22: 0x10e3c52 in posixCallMainAndExit (runtime_wrong_union_field_access)
root.main();
^/home/andy/dev/zig/lib/std/start.zig:271:5: 0x10e382d in _start (runtime_wrong_union_field_access)
asm volatile (switch (native_arch) {
^???:?:?: 0x0 in ??? (???)
(process terminated by signal)
$ zig test test_comptime_out_of_bounds_float_to_integer_cast.zig/home/andy/dev/zig/doc/langref/test_comptime_out_of_bounds_float_to_integer_cast.zig:3:36: error: float value '4294967296' cannot be stored in integer type 'i32'
const int: i32 = @intFromFloat(float);
^~~~~
$ zig build-exe runtime_out_of_bounds_float_to_integer_cast.zig
$ ./runtime_out_of_bounds_float_to_integer_cast
thread 200414 panic: integer part of floating point value out of bounds
/home/andy/dev/zig/doc/langref/runtime_out_of_bounds_float_to_integer_cast.zig:4:22: 0x10de8d9 in main (runtime_out_of_bounds_float_to_integer_cast)
const int: i32 = @intFromFloat(float);
^/home/andy/dev/zig/lib/std/start.zig:651:22: 0x10de272 in posixCallMainAndExit (runtime_out_of_bounds_float_to_integer_cast)
root.main();
^/home/andy/dev/zig/lib/std/start.zig:271:5: 0x10dde4d in _start (runtime_out_of_bounds_float_to_integer_cast)
asm volatile (switch (native_arch) {
^???:?:?: 0x0 in ??? (???)
(process terminated by signal)
$ zig test test_string_literal_to_const_slice.zig
1/1 test_string_literal_to_const_slice.test.string literal to constant slice...OK
All 1 tests passed.
//! Because this file contains fields, it is a type which is intended to be instantiated, and so//! is named in TitleCase instead of snake_case by convention.
foo: u32,
bar: u64,
/// `@This()` can be used to refer to this struct type. In files with fields, it is quite common to/// name the type here, so it can be easily referenced by other declarations in this file.const TopLevelFields = @This();
pubfninit(val: u32) TopLevelFields {
return .{
.foo = val,
.bar = val * 10,
};
}
comptime {
// This will ensure that the file 'api.zig' is always discovered (as long as this file is discovered).// It is useful if 'api.zig' contains important exported declarations.
_ = @import("api.zig");
// We could also have a file which contains declarations we only want to export depending on a comptime// condition. In that case, we can use an `if` statement here:if (builtin.os.tag == .windows) {
_ = @import("windows_api.zig");
}
}
test {
// This will ensure that the file 'tests.zig' is always discovered (as long as this file is discovered),// if this compilation is a test. It is useful if 'tests.zig' contains tests we want to ensure are run.
_ = @import("tests.zig");
// We could also have a file which contains tests we only want to run depending on a comptime condition.// In that case, we can use an `if` statement here:if (builtin.os.tag == .windows) {
_ = @import("windows_tests.zig");
}
}
const builtin = @import("builtin");
/// `std.start` imports this file using `@import("root")`, and uses this declaration as the program's/// user-provided entry point. It can return any of the following types:/// * `void`/// * `E!void`, for any error set `E`/// * `u8`/// * `E!u8`, for any error set `E`/// Returning a `void` value from this function will exit with code 0./// Returning a `u8` value from this function will exit with the given status code./// Returning an error value from this function will print an Error Return Trace and exit with code 1.pubfnmain() void {
std.debug.print("Hello, World!\n", .{});
}
// If uncommented, this declaration would suppress the usual std.start logic, causing// the `main` declaration above to be ignored.//pub const _start = {};const std = @import("std");
/// The presence of this declaration allows the program to override certain behaviors of the standard library./// For a full list of available options, see the documentation for `std.Options`.pubconst std_options: std.Options = .{
// By default, in safe build modes, the standard library will attach a segfault handler to the program to// print a helpful stack trace if a segmentation fault occurs. Here, we can disable this, or even enable// it in unsafe build modes.
.enable_segfault_handler = true,
// This is the logging function used by `std.log`.
.logFn = myLogFn,
};
fnmyLogFn(
comptime level: std.log.Level,
comptime scope: @Type(.enum_literal),
comptime format: []constu8,
args: anytype,
) void {
// We could do anything we want here!// ...but actually, let's just call the default implementation.
std.log.defaultLog(level, scope, format, args);
}
const std = @import("std");
// This header is generated by zig from mathtest.zig
#include "mathtest.h"
#include <stdio.h>
int main(int argc, char **argv) {
int32_t result = add(42, 1337);
printf("%d\n", result);
return 0;
}