@@ -363,26 +363,15 @@
{#header_open|Hello World#}
{#code_begin|exe|hello#}
const std = @import("std");
{#code|hello.zig#}
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
try stdout.print("Hello, {s}!\n", .{"world"});
}
{#code_end#}
<p>
Most of the time, it is more appropriate to write to stderr rather than stdout, and
whether or not the message is successfully written to the stream is irrelevant.
For this common case, there is a simpler API:
</p>
{#code_begin|exe|hello_again#}
const std = @import("std");
{#code|hello_again.zig#}
pub fn main() void {
std.debug.print("Hello, world!\n", .{});
}
{#code_end#}
<p>
In this case, the {#syntax#}!{#endsyntax#} may be omitted from the return
type because no errors are returned from the function.
@@ -398,18 +387,8 @@ pub fn main() void {
The generated documentation is still experimental, and can be produced with:
</p>
{#shell_samp#}zig test -femit-docs main.zig{#end_shell_samp#}
{#code_begin|exe|comments#}
const print = @import("std").debug.print;
{#code|comments.zig#}
pub fn main() 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
}
{#code_end#}
<p>
There are no multiline comments in Zig (e.g. like <code class="c">/* */</code>
comments in C). This allows Zig to have the property that each line
@@ -422,40 +401,17 @@ pub fn main() void {
multiple doc comments in a row are merged together to form a multiline
doc comment. The doc comment documents whatever immediately follows it.
</p>
{#code_begin|syntax|doc_comments#}
/// 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,
{#code|doc_comments.zig#}
/// 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).
pub fn unixEpoch() Timestamp {
return Timestamp{
.seconds = 0,
.nanos = 0,
};
}
};
{#code_end#}
<p>
Doc comments are only allowed in certain places; it is a compile error to
have a doc comment in an unexpected place, such as in the middle of an expression,
or just before a non-doc comment.
</p>
{#code_begin|obj_err|invalid_doc-comment|expected type expression, found 'a document comment'#}
/// doc-comment
//! top-level doc-comment
const std = @import("std");
{#code_end#}
{#code_begin|obj_err|unattached_doc-comment|unattached documentation comment#}
pub fn main() void {}
{#code|invalid_doc-comment.zig#}
{#code|unattached_doc-comment.zig#}
/// End of file
{#code_end#}
<p>
Doc comments can be interleaved with normal comments. Currently, when producing
the package documentation, normal comments are merged with doc comments.
@@ -470,71 +426,13 @@ pub fn main() void {}
It is a compile error if a top-level doc comment is not placed at the start
of a {#link|container|Containers#}, before any expressions.
</p>
{#code_begin|syntax|tldoc_comments#}
//! 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.
{#code|tldoc_comments.zig#}
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.
};
{#code_end#}
{#header_close#}
{#header_close#}
{#header_open|Values#}
{#code_begin|exe|values#}
// Top-level declarations are order-independent:
const print = std.debug.print;
const std = @import("std");
const os = std.os;
const assert = std.debug.assert;
{#code|values.zig#}
pub fn main() void {
// integers
const one_plus_one: i32 = 1 + 1;
print("1 + 1 = {}\n", .{one_plus_one});
// floats
const seven_div_three: f32 = 7.0 / 3.0;
print("7.0 / 3.0 = {}\n", .{seven_div_three});
// boolean
print("{}\n{}\n{}\n", .{
true and false,
true or false,
!true,
});
// optional
var optional_value: ?[]const u8 = null;
assert(optional_value == null);
print("\noptional 1\ntype: {}\nvalue: {?s}\n", .{
@TypeOf(optional_value), optional_value,
});
optional_value = "hi";
assert(optional_value != null);
print("\noptional 2\ntype: {}\nvalue: {?s}\n", .{
@TypeOf(optional_value), optional_value,
});
// error union
var number_or_error: anyerror!i32 = error.ArgNotFound;
print("\nerror union 1\ntype: {}\nvalue: {!}\n", .{
@TypeOf(number_or_error), number_or_error, });
number_or_error = 1234;
print("\nerror union 2\ntype: {}\nvalue: {!}\n", .{
@TypeOf(number_or_error), number_or_error,
});
}
{#code_end#}
{#header_open|Primitive Types#}
<div class="table-wrapper">
<table>
@@ -785,27 +683,8 @@ pub fn main() void {
{#link|Integer Literals#}. All {#link|Escape Sequences#} are valid in both string literals
and Unicode code point literals.
</p>
{#code_begin|exe|string_literals#}
const print = @import("std").debug.print;
const mem = @import("std").mem; // will be used to compare bytes
{#code|string_literals.zig#}
pub fn main() void {
const bytes = "hello";
print("{}\n", .{@TypeOf(bytes)}); // *const [5:0]u8
print("{d}\n", .{bytes.len}); // 5
print("{c}\n", .{bytes[1]}); // 'e'
print("{d}\n", .{bytes[5]}); // 0
print("{}\n", .{'e' == '\x65'}); // true
print("{d}\n", .{'\u{1f4a9}'}); // 128169
print("{d}\n", .{'💯'}); // 128175
print("{u}\n", .{'âš¡'});
print("{}\n", .{mem.eql(u8, "hello", "h\x65llo")}); // true
print("{}\n", .{mem.eql(u8, "💯", "\xf0\x9f\x92\xaf")}); // also true
const invalid_utf8 = "\xff\xfe"; // non-UTF-8 strings are possible with \xNN notation.
print("0x{x}\n", .{invalid_utf8[1]}); // indexing them returns individual bytes...
print("0x{x}\n", .{"💯"[1]}); // ...as does indexing part-way through non-ASCII characters
}
{#code_end#}
{#see_also|Arrays|Source Encoding#}
{#header_open|Escape Sequences#}
<div class="table-wrapper">
@@ -864,68 +743,26 @@ pub fn main() void {
However, if the next line begins with {#syntax#}\\{#endsyntax#} then a newline is appended and
the string literal continues.
</p>
{#code_begin|syntax|multiline_string_literals#}
const hello_world_in_c =
\\#include <stdio.h>
\\
\\int main(int argc, char **argv) {
\\ printf("hello world\n");
\\ return 0;
\\}
;
{#code_end#}
{#code|multiline_string_literals.zig#}
{#see_also|@embedFile#}
{#header_close#}
{#header_close#}
{#header_open|Assignment#}
<p>Use the {#syntax#}const{#endsyntax#} keyword to assign a value to an identifier:</p>
{#code_begin|exe_build_err|constant_identifier_cannot_change#}
const x = 1234;
{#code|constant_identifier_cannot_change.zig#}
fn foo() void {
// It works at file scope as well as inside functions.
const y = 5678;
// Once assigned, an identifier cannot be changed.
y += 1;
}
pub fn main() void {
foo();
}
{#code_end#}
<p>{#syntax#}const{#endsyntax#} applies to all of the bytes that the identifier immediately addresses. {#link|Pointers#} have their own const-ness.</p>
<p>If you need a variable that you can modify, use the {#syntax#}var{#endsyntax#} keyword:</p>
{#code_begin|exe|mutable_var#}
const print = @import("std").debug.print;
{#code|mutable_var.zig#}
pub fn main() void {
var y: i32 = 5678;
y += 1;
print("{d}", .{y});
}
{#code_end#}
<p>Variables must be initialized:</p>
{#code_begin|exe_build_err|var_must_be_initialized#}
pub fn main() void {
var x: i32;
{#code|var_must_be_initialized.zig#}
x = 1;
}
{#code_end#}
{#header_open|undefined#}
<p>Use {#syntax#}undefined{#endsyntax#} to leave variables uninitialized:</p>
{#code_begin|exe|assign_undefined#}
const print = @import("std").debug.print;
{#code|assign_undefined.zig#}
pub fn main() void {
var x: i32 = undefined;
x = 1;
print("{d}", .{x});
}
{#code_end#}
<p>
{#syntax#}undefined{#endsyntax#} can be {#link|coerced|Type Coercion#} to any type.
Once this happens, it is no longer possible to detect that the value is {#syntax#}undefined{#endsyntax#}.
@@ -945,29 +782,8 @@ pub fn main() void {
<p>
Code written within one or more {#syntax#}test{#endsyntax#} declarations can be used to ensure behavior meets expectations:
</p>
{#code_begin|test|testing_introduction#}
const std = @import("std");
{#code|testing_introduction.zig#}
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.
fn addOne(number: i32) i32 {
return number + 1;
}
{#code_end#}
<p>
The <code class="file">testing_introduction.zig</code> code sample tests the {#link|function|Functions#}
{#syntax#}addOne{#endsyntax#} to ensure that it returns {#syntax#}42{#endsyntax#} given the input
@@ -1044,17 +860,8 @@ fn addOne(number: i32) i32 {
When a test returns an error, the test is considered a failure and its {#link|error return trace|Error Return Traces#}
is output to standard error. The total number of failures will be reported after all tests have run.
</p>
{#code_begin|test_err|testing_failure#}
const std = @import("std");
{#code|testing_failure.zig#}
test "expect this to fail" {
try std.testing.expect(false);
}
test "expect this to succeed" {
try std.testing.expect(true);
}
{#code_end#}
{#header_close#}
{#header_open|Skip Tests#}
<p>
@@ -1068,11 +875,8 @@ test "expect this to succeed" {
{#syntax#}error.SkipZigTest{#endsyntax#} and the default test runner will consider the test as being skipped.
The total number of skipped tests will be reported after all tests have run.
</p>
{#code_begin|test|testing_skip#}
test "this will be skipped" {
return error.SkipZigTest;
}
{#code_end#}
{#code|testing_skip.zig#}
{#header_close#}
{#header_open|Report Memory Leaks#}
@@ -1081,17 +885,8 @@ test "this will be skipped" {
{#syntax#}std.testing.allocator{#endsyntax#}, the default test runner will report any leaks that are
found from using the testing allocator:
</p>
{#code_begin|test_err|testing_detect_leak|1 tests leaked memory#}
const std = @import("std");
{#code|testing_detect_leak.zig#}
test "detect leak" {
var list = std.ArrayList(u21).init(std.testing.allocator);
// missing `defer list.deinit();`
try list.append('☔');
try std.testing.expect(list.items.len == 1);
}
{#code_end#}
{#see_also|defer|Memory#}
{#header_close#}
{#header_open|Detecting Test Build#}
@@ -1099,19 +894,8 @@ test "detect leak" {
Use the {#link|compile variable|Compile Variables#} {#syntax#}@import("builtin").is_test{#endsyntax#}
to detect a test build:
</p>
{#code_begin|test|testing_detect_test#}
const std = @import("std");
const builtin = @import("builtin");
const expect = std.testing.expect;
{#code|testing_detect_test.zig#}
test "builtin.is_test" {
try expect(isATest());
}
fn isATest() bool {
return builtin.is_test;
}
{#code_end#}
{#header_close#}
{#header_open|Test Output and Logging#}
<p>
@@ -1124,28 +908,8 @@ fn isATest() bool {
you create tests. In addition to the <code>expect</code> function, this document uses a couple of more functions
as exemplified here:
</p>
{#code_begin|test|testing_namespace#}
const std = @import("std");
{#code|testing_namespace.zig#}
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);
}
{#code_end#}
<p>The Zig Standard Library also contains functions to compare {#link|Slices#}, strings, and more. See the rest of the
{#syntax#}std.testing{#endsyntax#} namespace in the {#link|Zig Standard Library#} for more available functions.</p>
{#header_close#}
@@ -1186,20 +950,8 @@ test "expectError demo" {
<p>
If a name that does not fit these requirements is needed, such as for linking with external libraries, the {#syntax#}@""{#endsyntax#} syntax may be used.
</p>
{#code_begin|syntax|identifiers#}
const @"identifier with spaces in it" = 0xff;
const @"1SmallStep4Man" = 112358;
{#code|identifiers.zig#}
const c = @import("std").c;
pub extern "c" fn @"error"() void;
pub extern "c" fn @"fstat$INODE64"(fd: c.fd_t, buf: *c.Stat) c_int;
const Color = enum {
red,
@"really red",
};
const color: Color = .@"really red";
{#code_end#}
{#header_close#}
{#header_open|Container Level Variables#}
@@ -1209,92 +961,29 @@ const color: Color = .@"really red";
{#link|comptime#}. If a container level variable is {#syntax#}const{#endsyntax#} then its value is
{#syntax#}comptime{#endsyntax#}-known, otherwise it is runtime-known.
</p>
{#code_begin|test|test_container_level_variables#}
var y: i32 = add(10, x);
const x: i32 = add(12, 34);
{#code|test_container_level_variables.zig#}
test "container level variables" {
try expect(x == 46);
try expect(y == 56);
}
fn add(a: i32, b: i32) i32 {
return a + b;
}
const std = @import("std");
const expect = std.testing.expect;
{#code_end#}
<p>
Container level variables may be declared inside a {#link|struct#}, {#link|union#}, {#link|enum#}, or {#link|opaque#}:
</p>
{#code_begin|test|test_namespaced_container_level_variable#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_namespaced_container_level_variable.zig#}
test "namespaced container level variable" {
try expect(foo() == 1235);
try expect(foo() == 1236);
}
const S = struct {
var x: i32 = 1234;
};
fn foo() i32 {
S.x += 1;
return S.x;
}
{#code_end#}
{#header_close#}
{#header_open|Static Local Variables#}
<p>
It is also possible to have local variables with static lifetime by using containers inside functions.
</p>
{#code_begin|test|test_static_local_variable#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_static_local_variable.zig#}
test "static local variable" {
try expect(foo() == 1235);
try expect(foo() == 1236);
}
fn foo() i32 {
const S = struct {
var x: i32 = 1234;
};
S.x += 1;
return S.x;
}
{#code_end#}
{#header_close#}
{#header_open|Thread Local Variables#}
<p>A variable may be specified to be a thread-local variable using the
{#syntax#}threadlocal{#endsyntax#} keyword,
which makes each thread work with a separate instance of the variable:</p>
{#code_begin|test|test_thread_local_variables#}
const std = @import("std");
const assert = std.debug.assert;
{#code|test_thread_local_variables.zig#}
threadlocal var x: i32 = 1234;
test "thread local storage" {
const thread1 = try std.Thread.spawn(.{}, testTls, .{});
const thread2 = try std.Thread.spawn(.{}, testTls, .{});
testTls();
thread1.join();
thread2.join();
}
fn testTls() void {
assert(x == 1234);
x += 1;
assert(x == 1235);
}
{#code_end#}
<p>
For {#link|Single Threaded Builds#}, all thread local variables are treated as regular {#link|Container Level Variables#}.
</p>
@@ -1319,45 +1008,15 @@ fn testTls() void {
All variables declared in a {#syntax#}comptime{#endsyntax#} expression are implicitly
{#syntax#}comptime{#endsyntax#} variables.
</p>
{#code_begin|test|test_comptime_variables#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_comptime_variables.zig#}
test "comptime vars" {
var x: i32 = 1;
comptime var 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");
}
}
{#code_end#}
{#header_close#}
{#header_close#}
{#header_open|Integers#}
{#header_open|Integer Literals#}
{#code_begin|syntax|integer_literals#}
const decimal_int = 98222;
const hex_int = 0xff;
const another_hex_int = 0xFF;
const octal_int = 0o755;
const binary_int = 0b11110000;
{#code|integer_literals.zig#}
// underscores may be placed between two digits as a visual separator
const one_billion = 1_000_000_000;
const binary_mask = 0b1_1111_1111;
const permissions = 0o7_5_5;
const big_address = 0xFF80_0000_0000_0000;
{#code_end#}
{#header_close#}
{#header_open|Runtime Integer Values#}
<p>
@@ -1368,11 +1027,8 @@ const big_address = 0xFF80_0000_0000_0000;
However, once an integer value is no longer known at compile-time, it must have a
known size, and is vulnerable to undefined behavior.
</p>
{#code_begin|syntax|runtime_vs_comptime#}
fn divide(a: i32, b: i32) i32 {
return a / b;
}
{#code_end#}
{#code|runtime_vs_comptime.zig#}
<p>
In this function, values {#syntax#}a{#endsyntax#} and {#syntax#}b{#endsyntax#} are known only at runtime,
and thus this division operation is vulnerable to both {#link|Integer Overflow#} and
@@ -1414,66 +1070,25 @@ fn divide(a: i32, b: i32) i32 {
Float literals {#link|coerce|Type Coercion#} to any floating point type,
and to any {#link|integer|Integers#} type when there is no fractional component.
</p>
{#code_begin|syntax|float_literals#}
const floating_point = 123.0E+77;
const another_float = 123.0;
const yet_another = 123.0e+77;
{#code|float_literals.zig#}
const hex_floating_point = 0x103.70p-5;
const another_hex_float = 0x103.70;
const yet_another_hex_float = 0x103.70P-5;
// underscores may be placed between two digits as a visual separator
const lightspeed = 299_792_458.000_000;
const nanosecond = 0.000_000_001;
const more_hex = 0x1234_5678.9ABC_CDEFp-10;
{#code_end#}
<p>
There is no syntax for NaN, infinity, or negative infinity. For these special values,
one must use the standard library:
</p>
{#code_begin|syntax|float_special_values#}
const std = @import("std");
{#code|float_special_values.zig#}
const inf = std.math.inf(f32);
const negative_inf = -std.math.inf(f64);
const nan = std.math.nan(f128);
{#code_end#}
{#header_close#}
{#header_open|Floating Point Operations#}
<p>By default floating point operations use {#syntax#}Strict{#endsyntax#} mode,
but you can switch to {#syntax#}Optimized{#endsyntax#} mode on a per-block basis:</p>
{#code_begin|obj|float_mode_obj#}
{#code_release_fast#}
{#code_disable_cache#}
const std = @import("std");
const big = @as(f64, 1 << 40);
{#code|float_mode_obj.zig#}
export fn foo_strict(x: f64) f64 {
return x + big - big;
}
export fn foo_optimized(x: f64) f64 {
@setFloatMode(.optimized);
return x + big - big;
}
{#code_end#}
<p>For this test we have to separate code into two object files -
otherwise the optimizer figures out all the values at compile-time,
which operates in strict mode.</p>
{#code_begin|exe|float_mode_exe#}
{#code_link_object|float_mode_obj#}
const print = @import("std").debug.print;
{#code|float_mode_exe.zig#}
extern fn foo_strict(x: f64) f64;
extern fn foo_optimized(x: f64) f64;
pub fn main() void {
const x = 0.001;
print("optimized = {}\n", .{foo_optimized(x)});
print("strict = {}\n", .{foo_strict(x)});
}
{#code_end#}
{#see_also|@setFloatMode|Division by Zero#}
{#header_close#}
{#header_close#}
@@ -2241,141 +1856,16 @@ or
{#header_close#}
{#header_close#}
{#header_open|Arrays#}
{#code_begin|test|test_arrays#}
const expect = @import("std").testing.expect;
const assert = @import("std").debug.assert;
const mem = @import("std").mem;
{#code|test_arrays.zig#}
// array literal
const message = [_]u8{ 'h', 'e', 'l', 'l', 'o' };
// get the size of an array
comptime {
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 array
var 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 time
const 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 arrays
const hello = "hello";
const world = "world";
const hello_world = hello ++ " " ++ world;
comptime {
assert(mem.eql(u8, hello_world, "hello world"));
}
// ** does repeating patterns
const pattern = "ab" ** 3;
comptime {
assert(mem.eql(u8, pattern, "ababab"));
}
// initialize an array to zero
const all_zero = [_]u16{0} ** 10;
comptime {
assert(all_zero.len == 10);
assert(all_zero[5] == 0);
}
// use compile-time code to initialize an array
var 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 array
var more_points = [_]Point{makePoint(3)} ** 10;
fn makePoint(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);
}
{#code_end#}
{#see_also|for|Slices#}
{#header_open|Multidimensional Arrays#}
<p>
Multidimensional arrays can be created by nesting arrays:
</p>
{#code_begin|test|test_multidimensional_arrays#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_multidimensional_arrays.zig#}
const mat4x4 = [4][4]f32{
[_]f32{ 1.0, 0.0, 0.0, 0.0 },
[_]f32{ 0.0, 1.0, 0.0, 1.0 },
[_]f32{ 0.0, 0.0, 1.0, 0.0 },
[_]f32{ 0.0, 0.0, 0.0, 1.0 },
};
test "multidimensional arrays" {
// Access the 2D array by indexing the outer array, and then the inner array.
try expect(mat4x4[1][1] == 1.0);
// Here we iterate with for loops.
for (mat4x4, 0..) |row, row_index| {
for (row, 0..) |cell, column_index| {
if (row_index == column_index) {
try expect(cell == 1.0);
}
}
}
}
{#code_end#}
{#header_close#}
{#header_open|Sentinel-Terminated Arrays#}
@@ -2383,27 +1873,8 @@ test "multidimensional arrays" {
The syntax {#syntax#}[N:x]T{#endsyntax#} describes an array which has a sentinel element of value {#syntax#}x{#endsyntax#} at the
index corresponding to the length {#syntax#}N{#endsyntax#}.
</p>
{#code_begin|test|test_null_terminated_array#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_null_terminated_array.zig#}
test "0-terminated sentinel array" {
const array = [_:0]u8 {1, 2, 3, 4};
try expect(@TypeOf(array) == [4:0]u8);
try expect(array.len == 4);
try expect(array[4] == 0);
}
test "extra 0s in 0-terminated sentinel array" {
// The sentinel value may appear earlier, but does not influence the compile-time 'len'.
const array = [_:0]u8 {1, 0, 0, 4};
try expect(@TypeOf(array) == [4:0]u8);
try expect(array.len == 4);
try expect(array[4] == 0);
}
{#code_end#}
{#see_also|Sentinel-Terminated Pointers|Sentinel-Terminated Slices#}
{#header_close#}
{#header_close#}
@@ -2445,47 +1916,8 @@ test "extra 0s in 0-terminated sentinel array" {
although small powers of two (2-64) are most typical. Note that excessively long vector lengths (e.g. 2^20) may
result in compiler crashes on current versions of Zig.
</p>
{#code_begin|test|test_vector#}
const std = @import("std");
const expectEqual = std.testing.expectEqual;
{#code|test_vector.zig#}
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 forth
const 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: []const f32 = &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 length
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);
}
{#code_end#}
<p>
TODO talk about C ABI interop<br>
TODO consider suggesting std.MultiArrayList
@@ -2534,76 +1966,13 @@ test "Conversion between vectors, arrays, and slices" {
</li>
</ul>
<p>Use {#syntax#}&x{#endsyntax#} to obtain a single-item pointer:</p>
{#code_begin|test|test_single_item_pointer#}
const expect = @import("std").testing.expect;
{#code|test_single_item_pointer.zig#}
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) == *const i32);
// 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);
}
{#code_end#}
<p>
Zig supports pointer arithmetic. It's better to assign the pointer to {#syntax#}[*]T{#endsyntax#} and increment that variable. For example, directly incrementing the pointer from a slice will corrupt it.
</p>
{#code_begin|test|test_pointer_arithmetic#}
const expect = @import("std").testing.expect;
{#code|test_pointer_arithmetic.zig#}
test "pointer arithmetic with many-item pointer" {
const array = [_]i32{ 1, 2, 3, 4 };
var ptr: [*]const i32 = &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);
}
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' error
var 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 updated
try expect(slice[0] == 2);
try expect(slice.len == 4);
}
{#code_end#}
<p>
In Zig, we generally prefer {#link|Slices#} rather than {#link|Sentinel-Terminated Pointers#}.
You can turn an array or pointer into a slice using slice syntax.
@@ -2613,78 +1982,28 @@ test "pointer arithmetic with slices" {
against this kind of undefined behavior. This is one reason
we prefer slices to pointers.
</p>
{#code_begin|test|test_slice_bounds#}
const expect = @import("std").testing.expect;
{#code|test_slice_bounds.zig#}
test "pointer slicing" {
var array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var start: usize = 2; // var to make it runtime-known
_ = &start; // suppress 'var is never mutated' error
const slice = array[start..4];
try expect(slice.len == 2);
try expect(array[3] == 4);
slice[1] += 1;
try expect(array[3] == 5);
}
{#code_end#}
<p>Pointers work at compile-time too, as long as the code does not depend on
an undefined memory layout:</p>
{#code_begin|test|test_comptime_pointers#}
const expect = @import("std").testing.expect;
{#code|test_comptime_pointers.zig#}
test "comptime pointers" {
comptime {
var x: i32 = 1;
const ptr = &x;
ptr.* += 1;
x += 1;
try expect(ptr.* == 3);
}
}
{#code_end#}
<p>To convert an integer address into a pointer, use {#syntax#}@ptrFromInt{#endsyntax#}.
To convert a pointer to an integer, use {#syntax#}@intFromPtr{#endsyntax#}:</p>
{#code_begin|test|test_integer_pointer_conversion#}
const expect = @import("std").testing.expect;
{#code|test_integer_pointer_conversion.zig#}
test "@intFromPtr and @ptrFromInt" {
const ptr: *i32 = @ptrFromInt(0xdeadbee0);
const addr = @intFromPtr(ptr);
try expect(@TypeOf(addr) == usize);
try expect(addr == 0xdeadbee0);
}
{#code_end#}
<p>Zig is able to preserve memory addresses in comptime code, as long as
the pointer is never dereferenced:</p>
{#code_begin|test|test_comptime_pointer_conversion#}
const expect = @import("std").testing.expect;
{#code|test_comptime_pointer_conversion.zig#}
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);
}
}
{#code_end#}
{#see_also|Optional Pointers|@ptrFromInt|@intFromPtr|C Pointers#}
{#header_open|volatile#}
<p>Loads and stores are assumed to not have side effects. If a given load or store
should have side effects, such as Memory Mapped Input/Output (MMIO), use {#syntax#}volatile{#endsyntax#}.
In the following code, loads and stores with {#syntax#}mmio_ptr{#endsyntax#} are guaranteed to all happen
and in the same order as in source code:</p>
{#code_begin|test|test_volatile#}
const expect = @import("std").testing.expect;
{#code|test_volatile.zig#}
test "volatile" {
const mmio_ptr: *volatile u8 = @ptrFromInt(0x12345678);
try expect(@TypeOf(mmio_ptr) == *volatile u8);
}
{#code_end#}
<p>
Note that {#syntax#}volatile{#endsyntax#} is unrelated to concurrency and {#link|Atomics#}.
If you see code that is using {#syntax#}volatile{#endsyntax#} for something other than Memory Mapped
@@ -2698,29 +2017,8 @@ test "volatile" {
kinds of type conversions are preferable to
{#syntax#}@ptrCast{#endsyntax#} if possible.
</p>
{#code_begin|test|test_pointer_casting#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_pointer_casting.zig#}
test "pointer casting" {
const bytes align(@alignOf(u32)) = [_]u8{ 0x12, 0x12, 0x12, 0x12 };
const u32_ptr: *const u32 = @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);
}
{#code_end#}
{#header_open|Alignment#}
<p>
Each type has an <strong>alignment</strong> - a number of bytes such that,
@@ -2736,21 +2034,8 @@ test "pointer child type" {
In Zig, a pointer type has an alignment value. If the value is equal to the
alignment of the underlying type, it can be omitted from the type:
</p>
{#code_begin|test|test_variable_alignment#}
const std = @import("std");
const builtin = @import("builtin");
const expect = std.testing.expect;
{#code|test_variable_alignment.zig#}
test "variable alignment" {
var x: i32 = 1234;
const align_of_i32 = @alignOf(@TypeOf(x));
try expect(@TypeOf(&x) == *i32);
try expect(*i32 == *align(align_of_i32) i32);
if (builtin.target.cpu.arch == .x86_64) {
try expect(@typeInfo(*i32).Pointer.alignment == 4);
}
}
{#code_end#}
<p>In the same way that a {#syntax#}*i32{#endsyntax#} can be {#link|coerced|Type Coercion#} to a
{#syntax#}*const i32{#endsyntax#}, a pointer with a larger alignment can be implicitly
cast to a pointer with a smaller alignment, but not vice versa.
@@ -2759,60 +2044,16 @@ test "variable alignment" {
You can specify alignment on variables and functions. If you do this, then
pointers to them get the specified alignment:
</p>
{#code_begin|test|test_variable_func_alignment#}
const expect = @import("std").testing.expect;
{#code|test_variable_func_alignment.zig#}
var foo: u8 align(4) = 100;
test "global variable alignment" {
try expect(@typeInfo(@TypeOf(&foo)).Pointer.alignment == 4);
try expect(@TypeOf(&foo) == *align(4) u8);
const as_pointer_to_array: *align(4) [1]u8 = &foo;
const as_slice: []align(4) u8 = as_pointer_to_array;
const as_unaligned_slice: []u8 = as_slice;
try expect(as_unaligned_slice[0] == 100);
}
fn derp() align(@sizeOf(usize) * 2) i32 {
return 1234;
}
fn noop1() align(1) void {}
fn noop4() align(4) void {}
test "function alignment" {
try expect(derp() == 1234);
try expect(@TypeOf(derp) == fn () i32);
try expect(@TypeOf(&derp) == *align(@sizeOf(usize) * 2) const fn () i32);
noop1();
try expect(@TypeOf(noop1) == fn () void);
try expect(@TypeOf(&noop1) == *align(1) const fn () void);
noop4();
try expect(@TypeOf(noop4) == fn () void);
try expect(@TypeOf(&noop4) == *align(4) const fn () void);
}
{#code_end#}
<p>
If you have a pointer or a slice that has a small alignment, but you know that it actually
has a bigger alignment, use {#link|@alignCast#} to change the
pointer into a more aligned pointer. This is a no-op at runtime, but inserts a
{#link|safety check|Incorrect Pointer Alignment#}:
</p>
{#code_begin|test_safety|test_incorrect_pointer_alignment|incorrect alignment#}
const std = @import("std");
{#code|test_incorrect_pointer_alignment.zig#}
test "pointer alignment safety" {
var array align(4) = [_]u32{ 0x11111111, 0x11111111 };
const bytes = std.mem.sliceAsBytes(array[0..]);
try std.testing.expect(foo(bytes) == 0x11111111);
}
fn foo(bytes: []u8) u32 {
const slice4 = bytes[1..5];
const int_slice = std.mem.bytesAsSlice(u32, @as([]align(4) u8, @alignCast(slice4)));
return int_slice[0];
}
{#code_end#}
{#header_close#}
{#header_open|allowzero#}
@@ -2824,17 +2065,8 @@ fn foo(bytes: []u8) u32 {
did not have the {#syntax#}allowzero{#endsyntax#} attribute, this would be a
{#link|Pointer Cast Invalid Null#} panic:
</p>
{#code_begin|test|test_allowzero#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_allowzero.zig#}
test "allowzero" {
var zero: usize = 0; // var to make to runtime-known
_ = &zero; // suppress 'var is never mutated' error
const ptr: *allowzero i32 = @ptrFromInt(zero);
try expect(@intFromPtr(ptr) == 0);
}
{#code_end#}
{#header_close#}
{#header_open|Sentinel-Terminated Pointers#}
@@ -2843,21 +2075,8 @@ test "allowzero" {
has a length determined by a sentinel value. This provides protection
against buffer overflow and overreads.
</p>
{#code_begin|exe_build_err|sentinel-terminated_pointer#}
{#link_libc#}
const std = @import("std");
{#code|sentinel-terminated_pointer.zig#}
// This is also available as `std.c.printf`.
pub extern "c" fn printf(format: [*:0]const u8, ...) c_int;
pub fn main() anyerror!void {
_ = printf("Hello, world!\n"); // OK
const msg = "Hello, world!\n";
const non_null_terminated_msg: [msg.len]u8 = msg.*;
_ = printf(&non_null_terminated_msg);
}
{#code_end#}
{#see_also|Sentinel-Terminated Slices|Sentinel-Terminated Arrays#}
{#header_close#}
{#header_close#}
@@ -2869,99 +2088,11 @@ pub fn main() anyerror!void {
compile-time, whereas the slice's length is known at runtime.
Both can be accessed with the `len` field.
</p>
{#code_begin|test_safety|test_basic_slices|index out of bounds#}
const expect = @import("std").testing.expect;
{#code|test_basic_slices.zig#}
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];
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.
}
{#code_end#}
<p>This is one reason we prefer slices to pointers.</p>
{#code_begin|test|test_slices#}
const std = @import("std");
const expect = std.testing.expect;
const mem = std.mem;
const fmt = std.fmt;
{#code|test_slices.zig#}
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 u8
const hello: []const u8 = "hello";
const world: []const u8 = "世界";
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);
}
{#code_end#}
{#see_also|Pointers|for|Arrays#}
{#header_open|Sentinel-Terminated Slices#}
@@ -2971,206 +2102,28 @@ test "slice pointer" {
guarantee that there are no sentinel elements before that. Sentinel-terminated slices allow element
access to the {#syntax#}len{#endsyntax#} index.
</p>
{#code_begin|test|test_null_terminated_slice#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_null_terminated_slice.zig#}
test "0-terminated slice" {
const slice: [:0]const u8 = "hello";
try expect(slice.len == 5);
try expect(slice[5] == 0);
}
{#code_end#}
<p>
Sentinel-terminated slices can also be created using a variation of the slice syntax
{#syntax#}data[start..end :x]{#endsyntax#}, where {#syntax#}data{#endsyntax#} is a many-item pointer,
array or slice and {#syntax#}x{#endsyntax#} is the sentinel value.
</p>
{#code_begin|test|test_null_terminated_slicing#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_null_terminated_slicing.zig#}
test "0-terminated slicing" {
var array = [_]u8{ 3, 2, 1, 0, 3, 2, 1, 0 };
var runtime_length: usize = 3;
_ = &runtime_length;
const slice = array[0..runtime_length :0];
try expect(@TypeOf(slice) == [:0]u8);
try expect(slice.len == 3);
}
{#code_end#}
<p>
Sentinel-terminated slicing asserts that the element in the sentinel position of the backing data is
actually the sentinel value. If this is not the case, safety-protected {#link|Undefined Behavior#} results.
</p>
{#code_begin|test_safety|test_sentinel_mismatch|sentinel mismatch#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_sentinel_mismatch.zig#}
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;
}
{#code_end#}
{#see_also|Sentinel-Terminated Pointers|Sentinel-Terminated Arrays#}
{#header_close#}
{#header_close#}
{#header_open|struct#}
{#code_begin|test|test_structs#}
// 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,
};
{#code|test_structs.zig#}
// Maybe we want to pass it to OpenGL so we want to be particular about
// how the bytes are arranged.
const Point2 = packed struct {
x: f32,
y: f32,
};
// Declare an instance of a struct.
const p = Point {
.x = 0.12,
.y = 0.34,
};
// Maybe we're not ready to fill out some of the fields.
var p2 = Point {
.x = 0.12,
.y = undefined,
};
// Structs can have methods
// Struct methods are not special, they are only namespaced
// functions that you can call with dot syntax.
const Vec3 = struct {
x: f32,
y: f32,
z: f32,
pub fn init(x: f32, y: f32, z: f32) Vec3 {
return Vec3 {
.x = x,
.y = y,
.z = z,
};
}
pub fn dot(self: Vec3, other: Vec3) f32 {
return self.x * other.x + self.y * other.y + self.z * other.z;
}
};
const expect = @import("std").testing.expect;
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 {
pub const PI = 3.14;
};
test "struct namespaced variable" {
try expect(Empty.PI == 3.14);
try expect(@sizeOf(Empty) == 0);
// you can still instantiate an empty struct
const does_nothing = Empty {};
_ = does_nothing;
}
// struct field order is determined by the compiler for optimal performance.
// however, you can still calculate a struct base pointer given a field pointer:
fn setYBasedOnX(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);
}
// You can return a struct from a function. This is how we do generics
// in Zig:
fn LinkedList(comptime T: type) type {
return struct {
pub const Node = struct {
prev: ?*Node,
next: ?*Node,
data: T,
};
first: ?*Node,
last: ?*Node,
len: usize,
};
}
test "linked list" {
// Functions called at compile-time are memoized. This means you can
// do this:
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 do
try expect(list2.first.?.data == 1234);
// instead of try expect(list2.first.?.*.data == 1234);
}
{#code_end#}
{#header_open|Default Field Values#}
<p>
@@ -3178,21 +2131,8 @@ test "linked list" {
value. Such expressions are executed at {#link|comptime#}, and allow the
field to be omitted in a struct literal expression:
</p>
{#code_begin|test|struct_default_field_values#}
const Foo = struct {
a: i32 = 1234,
b: i32,
};
{#code|struct_default_field_values.zig#}
test "default struct initialization fields" {
const x: Foo = .{
.b = 5,
};
if (x.a + x.b != 1239) {
comptime unreachable;
}
}
{#code_end#}
<p>
Default field values are only appropriate when the data invariants of a struct
cannot be violated by omitting that field from an initialization.
@@ -3200,32 +2140,8 @@ test "default struct initialization fields" {
<p>
For example, here is an inappropriate use of default struct field initialization:
</p>
{#code_begin|exe_err|bad_default_value#}
const Threshold = struct {
minimum: f32 = 0.25,
maximum: f32 = 0.75,
{#code|bad_default_value.zig#}
const Category = enum { low, medium, high };
fn categorize(t: Threshold, value: f32) Category {
assert(t.maximum >= t.minimum);
if (value < t.minimum) return .low;
if (value > t.maximum) return .high;
return .medium;
}
};
pub fn main() !void {
var threshold: Threshold = .{
.maximum = 0.20,
};
const category = threshold.categorize(0.90);
try std.io.getStdOut().writeAll(@tagName(category));
}
const std = @import("std");
const assert = std.debug.assert;
{#code_end#}
<p>
Above you can see the danger of ignoring this principle. The default
field values caused the data invariant to be violated, causing illegal
@@ -3235,17 +2151,8 @@ const assert = std.debug.assert;
To fix this, remove the default values from all the struct fields, and provide
a named default value:
</p>
{#code_begin|syntax|struct_default_value#}
const Threshold = struct {
minimum: f32,
maximum: f32,
{#code|struct_default_value.zig#}
const default: Threshold = .{
.minimum = 0.25,
.maximum = 0.75,
};
};
{#code_end#}
<p>If a struct value requires a runtime-known value in order to be initialized
without violating data invariants, then use an initialization method that accepts
those runtime values, and populates the remaining fields.</p>
@@ -3282,109 +2189,25 @@ const Threshold = struct {
in a {#link|@bitCast#} or a {#link|@ptrCast#} to reinterpret memory.
This even works at {#link|comptime#}:
</p>
{#code_begin|test|test_packed_structs#}
const std = @import("std");
const native_endian = @import("builtin").target.cpu.arch.endian();
const expect = std.testing.expect;
{#code|test_packed_structs.zig#}
const Full = packed struct {
number: u16,
};
const Divided = packed struct {
half1: u8,
quarter3: u4,
quarter4: u4,
};
test "@bitCast between packed structs" {
try doTheTest();
try comptime doTheTest();
}
fn doTheTest() !void {
try expect(@sizeOf(Full) == 2);
try expect(@sizeOf(Divided) == 2);
const full = Full{ .number = 0x1234 };
const divided: Divided = @bitCast(full);
try expect(divided.half1 == 0x34);
try expect(divided.quarter3 == 0x2);
try expect(divided.quarter4 == 0x1);
const ordered: [2]u8 = @bitCast(full);
switch (native_endian) {
.big => {
try expect(ordered[0] == 0x12);
try expect(ordered[1] == 0x34);
},
.little => {
try expect(ordered[0] == 0x34);
try expect(ordered[1] == 0x12);
},
}
}
{#code_end#}
<p>
The backing integer is inferred from the fields' total bit width.
Optionally, it can be explicitly provided and enforced at compile time:
</p>
{#code_begin|test_err|test_missized_packed_struct|backing integer type 'u32' has bit size 32 but the struct fields have a total bit size of 24#}
test "missized packed struct" {
const S = packed struct(u32) { a: u16, b: u8 };
_ = S{ .a = 4, .b = 2 };
}
{#code_end#}
{#code|test_missized_packed_struct.zig#}
<p>
Zig allows the address to be taken of a non-byte-aligned field:
</p>
{#code_begin|test|test_pointer_to_non-byte_aligned_field#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_pointer_to_non-byte_aligned_field.zig#}
const BitField = packed struct {
a: u3,
b: u3,
c: u2,
};
var foo = BitField{
.a = 1,
.b = 2,
.c = 3,
};
test "pointer to non-byte-aligned field" {
const ptr = &foo.b;
try expect(ptr.* == 2);
}
{#code_end#}
<p>
However, the pointer to a non-byte-aligned field has special properties and cannot
be passed when a normal pointer is expected:
</p>
{#code_begin|test_err|test_misaligned_pointer|expected type#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_misaligned_pointer.zig#}
const BitField = packed struct {
a: u3,
b: u3,
c: u2,
};
var bit_field = BitField{
.a = 1,
.b = 2,
.c = 3,
};
test "pointer to non-byte-aligned field" {
try expect(bar(&bit_field.b) == 2);
}
fn bar(x: *const u3) u3 {
return x.*;
}
{#code_end#}
<p>
In this case, the function {#syntax#}bar{#endsyntax#} cannot be called because the pointer
to the non-ABI-aligned field mentions the bit offset, but the function expects an ABI-aligned pointer.
@@ -3392,90 +2215,24 @@ fn bar(x: *const u3) u3 {
<p>
Pointers to non-ABI-aligned fields share the same address as the other fields within their host integer:
</p>
{#code_begin|test|test_packed_struct_field_address#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_packed_struct_field_address.zig#}
const BitField = packed struct {
a: u3,
b: u3,
c: u2,
};
var bit_field = BitField{
.a = 1,
.b = 2,
.c = 3,
};
test "pointers of sub-byte-aligned fields share addresses" {
try expect(@intFromPtr(&bit_field.a) == @intFromPtr(&bit_field.b));
try expect(@intFromPtr(&bit_field.a) == @intFromPtr(&bit_field.c));
}
{#code_end#}
<p>
This can be observed with {#link|@bitOffsetOf#} and {#link|offsetOf#}:
</p>
{#code_begin|test|test_bitOffsetOf_offsetOf#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_bitOffsetOf_offsetOf.zig#}
const BitField = packed struct {
a: u3,
b: u3,
c: u2,
};
test "offsets of non-byte-aligned fields" {
comptime {
try expect(@bitOffsetOf(BitField, "a") == 0);
try expect(@bitOffsetOf(BitField, "b") == 3);
try expect(@bitOffsetOf(BitField, "c") == 6);
try expect(@offsetOf(BitField, "a") == 0);
try expect(@offsetOf(BitField, "b") == 0);
try expect(@offsetOf(BitField, "c") == 0);
}
}
{#code_end#}
<p>
Packed structs have the same alignment as their backing integer, however, overaligned
pointers to packed structs can override this:
</p>
{#code_begin|test|test_overaligned_packed_struct#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_overaligned_packed_struct.zig#}
const S = packed struct {
a: u32,
b: u32,
};
test "overaligned pointer to packed struct" {
var foo: S align(4) = .{ .a = 1, .b = 2 };
const ptr: *align(4) S = &foo;
const ptr_to_b: *u32 = &ptr.b;
try expect(ptr_to_b.* == 2);
}
{#code_end#}
<p>
It's also possible to set alignment of struct fields:
</p>
{#code_begin|test|test_aligned_struct_fields#}
const std = @import("std");
const expectEqual = std.testing.expectEqual;
{#code|test_aligned_struct_fields.zig#}
test "aligned struct fields" {
const S = struct {
a: u32 align(2),
b: u32 align(64),
};
var foo = S{ .a = 1, .b = 2 };
try expectEqual(64, @alignOf(S));
try expectEqual(*align(2) u32, @TypeOf(&foo.a));
try expectEqual(*align(64) u32, @TypeOf(&foo.b));
}
{#code_end#}
<p>
Using packed structs with {#link|volatile#} is problematic, and may be a compile error in the future.
For details on this subscribe to
@@ -3497,22 +2254,8 @@ test "aligned struct fields" {
<li>If the struct is declared inside another struct, it gets named after both the parent
struct and the name inferred by the previous rules, separated by a dot.</li>
</ul>
{#code_begin|exe|struct_name#}
const std = @import("std");
{#code|struct_name.zig#}
pub fn main() void {
const Foo = struct {};
std.debug.print("variable: {s}\n", .{@typeName(Foo)});
std.debug.print("anonymous: {s}\n", .{@typeName(struct {})});
std.debug.print("function: {s}\n", .{@typeName(List(i32))});
}
fn List(comptime T: type) type {
return struct {
x: T,
};
}
{#code_end#}
{#header_close#}
{#header_open|Anonymous Struct Literals#}
@@ -3521,46 +2264,14 @@ fn List(comptime T: type) type {
the struct literal will directly instantiate the {#link|result location|Result Location Semantics#},
with no copy:
</p>
{#code_begin|test|test_struct_result#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_struct_result.zig#}
const Point = struct {x: i32, y: i32};
test "anonymous struct literal" {
const pt: Point = .{
.x = 13,
.y = 67,
};
try expect(pt.x == 13);
try expect(pt.y == 67);
}
{#code_end#}
<p>
The struct type can be inferred. Here the {#link|result location|Result Location Semantics#}
does not include a type, and so Zig infers the type:
</p>
{#code_begin|test|test_anonymous_struct#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_anonymous_struct.zig#}
test "fully anonymous struct" {
try check(.{
.int = @as(u32, 1234),
.float = @as(f64, 12.34),
.b = true,
.s = "hi",
});
}
fn check(args: anytype) !void {
try expect(args.int == 1234);
try expect(args.float == 12.34);
try expect(args.b);
try expect(args.s[0] == 'h');
try expect(args.s[1] == 'i');
}
{#code_end#}
{#header_close#}
{#header_open|Tuples#}
@@ -3577,193 +2288,36 @@ fn check(args: anytype) !void {
Like arrays, tuples have a .len field, can be indexed (provided the index is comptime-known)
and work with the ++ and ** operators. They can also be iterated over with {#link|inline for#}.
</p>
{#code_begin|test|test_tuples#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_tuples.zig#}
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');
}
{#code_end#}
{#header_close#}
{#see_also|comptime|@fieldParentPtr#}
{#header_close#}
{#header_open|enum#}
{#code_begin|test|test_enums#}
const expect = @import("std").testing.expect;
const mem = @import("std").mem;
{#code|test_enums.zig#}
// 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,
pub fn isClubs(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"));
}
{#code_end#}
{#see_also|@typeInfo|@tagName|@sizeOf#}
{#header_open|extern enum#}
<p>
By default, enums are not guaranteed to be compatible with the C ABI:
</p>
{#code_begin|obj_err|enum_export_error|parameter of type 'enum_export_error.Foo' not allowed in function with calling convention 'C'#}
const Foo = enum { a, b, c };
export fn entry(foo: Foo) void { _ = foo; }
{#code_end#}
{#code|enum_export_error.zig#}
<p>
For a C-ABI-compatible enum, provide an explicit tag type to
the enum:
</p>
{#code_begin|obj|enum_export#}
const Foo = enum(c_int) { a, b, c };
export fn entry(foo: Foo) void { _ = foo; }
{#code_end#}
{#code|enum_export.zig#}
{#header_close#}
{#header_open|Enum Literals#}
<p>
Enum literals allow specifying the name of an enum field without specifying the enum type:
</p>
{#code_begin|test|test_enum_literals#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_enum_literals.zig#}
const Color = enum {
auto,
off,
on,
};
test "enum literals" {
const color1: Color = .auto;
const color2 = Color.auto;
try expect(color1 == color2);
}
test "switch using enum literals" {
const color = Color.on;
const result = switch (color) {
.auto => false,
.on => true,
.off => false,
};
try expect(result);
}
{#code_end#}
{#header_close#}
{#header_open|Non-exhaustive enum#}
@@ -3780,33 +2334,8 @@ test "switch using enum literals" {
A switch on a non-exhaustive enum can include a {#syntax#}_{#endsyntax#} prong as an alternative to an {#syntax#}else{#endsyntax#} prong.
With a {#syntax#}_{#endsyntax#} prong the compiler errors if all the known tag names are not handled by the switch.
</p>
{#code_begin|test|test_switch_non-exhaustive#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_switch_non-exhaustive.zig#}
const Number = enum(u8) {
one,
two,
three,
_,
};
test "switch on non-exhaustive enum" {
const number = Number.one;
const result = switch (number) {
.one => true,
.two,
.three => false,
_ => false,
};
try expect(result);
const is_one = switch (number) {
.one => true,
else => false,
};
try expect(is_one);
}
{#code_end#}
{#header_close#}
{#header_close#}
@@ -3821,34 +2350,11 @@ test "switch on non-exhaustive enum" {
{#link|Accessing the non-active field|Wrong Union Field Access#} is
safety-checked {#link|Undefined Behavior#}:
</p>
{#code_begin|test_err|test_wrong_union_access|access of union field 'float' while field 'int' is active#}
const Payload = union {
int: i64,
float: f64,
boolean: bool,
};
test "simple union" {
var payload = Payload{ .int = 1234 };
payload.float = 12.34;
}
{#code_end#}
<p>You can activate another field by assigning the entire union:</p>
{#code_begin|test|test_simple_union#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_wrong_union_access.zig#}
<p>You can activate another field by assigning the entire union:</p>
{#code|test_simple_union.zig#}
const Payload = union {
int: i64,
float: f64,
boolean: bool,
};
test "simple union" {
var payload = Payload{ .int = 1234 };
try expect(payload.int == 1234);
payload = Payload{ .float = 12.34 };
try expect(payload.float == 12.34);
}
{#code_end#}
<p>
In order to use {#link|switch#} with a union, it must be a {#link|Tagged union#}.
</p>
@@ -3862,109 +2368,25 @@ test "simple union" {
to use with {#link|switch#} expressions.
Tagged unions coerce to their tag type: {#link|Type Coercion: Unions and Enums#}.
</p>
{#code_begin|test|test_tagged_union#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_tagged_union.zig#}
const ComplexTypeTag = enum {
ok,
not_ok,
};
const ComplexType = union(ComplexTypeTag) {
ok: u8,
not_ok: void,
};
test "switch on tagged union" {
const c = ComplexType{ .ok = 42 };
try expect(@as(ComplexTypeTag, c) == ComplexTypeTag.ok);
switch (c) {
ComplexTypeTag.ok => |value| try expect(value == 42),
ComplexTypeTag.not_ok => unreachable,
}
}
test "get tag type" {
try expect(std.meta.Tag(ComplexType) == ComplexTypeTag);
}
{#code_end#}
<p>In order to modify the payload of a tagged union in a switch expression,
place a {#syntax#}*{#endsyntax#} before the variable name to make it a pointer:
</p>
{#code_begin|test|test_switch_modify_tagged_union#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_switch_modify_tagged_union.zig#}
const ComplexTypeTag = enum {
ok,
not_ok,
};
const ComplexType = union(ComplexTypeTag) {
ok: u8,
not_ok: void,
};
test "modify tagged union in switch" {
var c = ComplexType{ .ok = 42 };
switch (c) {
ComplexTypeTag.ok => |*value| value.* += 1,
ComplexTypeTag.not_ok => unreachable,
}
try expect(c.ok == 43);
}
{#code_end#}
<p>
Unions can be made to infer the enum tag type.
Further, unions can have methods just like structs and enums.
</p>
{#code_begin|test|test_union_method#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_union_method.zig#}
const Variant = union(enum) {
int: i32,
boolean: bool,
// void can be omitted when inferring enum tag type.
none,
fn truthy(self: Variant) bool {
return switch (self) {
Variant.int => |x_int| x_int != 0,
Variant.boolean => |x_bool| x_bool,
Variant.none => false,
};
}
};
test "union method" {
var v1 = Variant{ .int = 1 };
var v2 = Variant{ .boolean = false };
try expect(v1.truthy());
try expect(!v2.truthy());
}
{#code_end#}
<p>
{#link|@tagName#} can be used to return a {#link|comptime#}
{#syntax#}[:0]const u8{#endsyntax#} value representing the field name:
</p>
{#code_begin|test|test_tagName#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_tagName.zig#}
const Small2 = union(enum) {
a: i32,
b: bool,
c: u8,
};
test "@tagName" {
try expect(std.mem.eql(u8, @tagName(Small2.a), "a"));
}
{#code_end#}
{#header_close#}
{#header_open|extern union#}
@@ -3983,26 +2405,8 @@ test "@tagName" {
{#header_open|Anonymous Union Literals#}
<p>{#link|Anonymous Struct Literals#} syntax can be used to initialize unions without specifying
the type:</p>
{#code_begin|test|test_anonymous_union#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_anonymous_union.zig#}
const Number = union {
int: i32,
float: f64,
};
test "anonymous union literal syntax" {
const i: Number = .{ .int = 42 };
const f = makeNumber();
try expect(i.int == 42);
try expect(f.float == 12.34);
}
fn makeNumber() Number {
return .{ .float = 12.34 };
}
{#code_end#}
{#header_close#}
{#header_close#}
@@ -4017,214 +2421,55 @@ fn makeNumber() Number {
This is typically used for type safety when interacting with C code that does not expose struct details.
Example:
</p>
{#code_begin|test_err|test_opaque|expected type '*test_opaque.Derp', found '*test_opaque.Wat'#}
const Derp = opaque {};
const Wat = opaque {};
{#code|test_opaque.zig#}
extern fn bar(d: *Derp) void;
fn foo(w: *Wat) callconv(.C) void {
bar(w);
}
test "call foo" {
foo(undefined);
}
{#code_end#}
{#header_close#}
{#header_open|Blocks#}
<p>
Blocks are used to limit the scope of variable declarations:
</p>
{#code_begin|test_err|test_blocks|use of undeclared identifier 'x'#}
test "access variable after block scope" {
{
var x: i32 = 1;
_ = &x;
}
x += 1;
}
{#code_end#}
{#code|test_blocks.zig#}
<p>Blocks are expressions. When labeled, {#syntax#}break{#endsyntax#} can be used
to return a value from the block:
</p>
{#code_begin|test|test_labeled_break#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_labeled_break.zig#}
test "labeled break from labeled block expression" {
var y: i32 = 123;
const x = blk: {
y += 1;
break :blk y;
};
try expect(x == 124);
try expect(y == 124);
}
{#code_end#}
<p>Here, {#syntax#}blk{#endsyntax#} can be any name.</p>
{#see_also|Labeled while|Labeled for#}
{#header_open|Shadowing#}
<p>{#link|Identifiers#} are never allowed to "hide" other identifiers by using the same name:</p>
{#code_begin|test_err|test_shadowing|local variable shadows declaration#}
const pi = 3.14;
{#code|test_shadowing.zig#}
test "inside test block" {
// Let's even go inside another block
{
var pi: i32 = 1234;
}
}
{#code_end#}
<p>
Because of this, when you read Zig code you can always rely on an identifier to consistently mean
the same thing within the scope it is defined. Note that you can, however, use the same name if
the scopes are separate:
</p>
{#code_begin|test|test_scopes#}
test "separate scopes" {
{
const pi = 3.14;
_ = pi;
}
{
var pi: bool = true;
_ = π
}
}
{#code_end#}
{#code|test_scopes.zig#}
{#header_close#}
{#header_open|Empty Blocks#}
<p>An empty block is equivalent to {#syntax#}void{}{#endsyntax#}:</p>
{#code_begin|test|test_empty_block#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_empty_block.zig#}
test {
const a = {};
const b = void{};
try expect(@TypeOf(a) == void);
try expect(@TypeOf(b) == void);
try expect(a == b);
}
{#code_end#}
{#header_close#}
{#header_close#}
{#header_open|switch#}
{#code_begin|test|test_switch#}
const std = @import("std");
const builtin = @import("builtin");
const expect = std.testing.expect;
{#code|test_switch.zig#}
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 => {},
}
}
{#code_end#}
<p>
{#syntax#}switch{#endsyntax#} can be used to capture the field values
of a {#link|Tagged union#}. Modifications to the field values can be
done by placing a {#syntax#}*{#endsyntax#} before the capture variable name,
turning it into a pointer.
</p>
{#code_begin|test|test_switch_tagged_union#}
const expect = @import("std").testing.expect;
{#code|test_switch_tagged_union.zig#}
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);
}
{#code_end#}
{#see_also|comptime|enum|@compileError|Compile Variables#}
{#header_open|Exhaustive Switching#}
@@ -4232,21 +2477,8 @@ test "switch on tagged union" {
When a {#syntax#}switch{#endsyntax#} expression does not have an {#syntax#}else{#endsyntax#} clause,
it must exhaustively list all the possible values. Failure to do so is a compile error:
</p>
{#code_begin|test_err|test_unhandled_enumeration_value|unhandled enumeration value#}
const Color = enum {
auto,
off,
on,
};
{#code|test_unhandled_enumeration_value.zig#}
test "exhaustive switching" {
const color = Color.off;
switch (color) {
Color.auto => {},
Color.on => {},
}
}
{#code_end#}
{#header_close#}
{#header_open|Switching with Enum Literals#}
@@ -4254,26 +2486,8 @@ test "exhaustive switching" {
{#link|Enum Literals#} can be useful to use with {#syntax#}switch{#endsyntax#} to avoid
repetitively specifying {#link|enum#} or {#link|union#} types:
</p>
{#code_begin|test|test_exhaustive_switch#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_exhaustive_switch.zig#}
const Color = enum {
auto,
off,
on,
};
test "enum literals with switch" {
const color = Color.off;
const result = switch (color) {
.auto => false,
.on => false,
.off => true,
};
try expect(result);
}
{#code_end#}
{#header_close#}
{#header_open|Inline Switch Prongs#}
@@ -4282,136 +2496,23 @@ test "enum literals with switch" {
the prong's body for each possible value it could have, making the
captured value {#link|comptime#}.
</p>
{#code_begin|test|test_inline_switch#}
const std = @import("std");
const expect = std.testing.expect;
const expectError = std.testing.expectError;
{#code|test_inline_switch.zig#}
fn isFieldOptional(comptime T: type, field_index: usize) !bool {
const fields = @typeInfo(T).Struct.fields;
return switch (field_index) {
// This prong is analyzed twice with `idx` being a
// comptime-known value each time.
inline 0, 1 => |idx| @typeInfo(fields[idx].type) == .Optional,
else => return error.IndexOutOfBounds,
};
}
const Struct1 = struct { a: u32, b: ?u32 };
test "using @typeInfo with runtime values" {
var index: usize = 0;
try expect(!try isFieldOptional(Struct1, index));
index += 1;
try expect(try isFieldOptional(Struct1, index));
index += 1;
try expectError(error.IndexOutOfBounds, isFieldOptional(Struct1, index));
}
// Calls to `isFieldOptional` on `Struct1` get unrolled to an equivalent
// of this function:
fn isFieldOptionalUnrolled(field_index: usize) !bool {
return switch (field_index) {
0 => false,
1 => true,
else => return error.IndexOutOfBounds,
};
}
{#code_end#}
<p>The {#syntax#}inline{#endsyntax#} keyword may also be combined with ranges:</p>
{#code_begin|syntax|inline_prong_range#}
fn isFieldOptional(comptime T: type, field_index: usize) !bool {
const fields = @typeInfo(T).Struct.fields;
return switch (field_index) {
inline 0...fields.len - 1 => |idx| @typeInfo(fields[idx].type) == .Optional,
else => return error.IndexOutOfBounds,
};
}
{#code_end#}
{#code|inline_prong_range.zig#}
<p>
{#syntax#}inline else{#endsyntax#} prongs can be used as a type safe
alternative to {#syntax#}inline for{#endsyntax#} loops:
</p>
{#code_begin|test|test_inline_else#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_inline_else.zig#}
const SliceTypeA = extern struct {
len: usize,
ptr: [*]u32,
};
const SliceTypeB = extern struct {
ptr: [*]SliceTypeA,
len: usize,
};
const AnySlice = union(enum) {
a: SliceTypeA,
b: SliceTypeB,
c: []const u8,
d: []AnySlice,
};
fn withFor(any: AnySlice) usize {
const Tag = @typeInfo(AnySlice).Union.tag_type.?;
inline for (@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;
}
fn withSwitch(any: AnySlice) usize {
return switch (any) {
// With `inline else` the function is explicitly generated
// as the desired switch and the compiler can check that
// every possible case is handled.
inline else => |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);
}
{#code_end#}
<p>
When using an inline prong switching on an union an additional
capture can be used to obtain the union's enum tag value.
</p>
{#code_begin|test|test_inline_switch_union_tag#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_inline_switch_union_tag.zig#}
const U = union(enum) {
a: u32,
b: f32,
};
fn getNum(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.
inline else => |num, tag| {
if (tag == .b) {
return @intFromFloat(num);
}
return num;
}
}
}
test "test" {
const u = U{ .b = 42 };
try expect(getNum(u) == 42);
}
{#code_end#}
{#see_also|inline while|inline for#}
{#header_close#}
{#header_close#}
@@ -4421,72 +2522,24 @@ test "test" {
A while loop is used to repeatedly execute an expression until
some condition is no longer true.
</p>
{#code_begin|test|test_while#}
const expect = @import("std").testing.expect;
{#code|test_while.zig#}
test "while basic" {
var i: usize = 0;
while (i < 10) {
i += 1;
}
try expect(i == 10);
}
{#code_end#}
<p>
Use {#syntax#}break{#endsyntax#} to exit a while loop early.
</p>
{#code_begin|test|test_while_break#}
const expect = @import("std").testing.expect;
{#code|test_while_break.zig#}
test "while break" {
var i: usize = 0;
while (true) {
if (i == 10)
break;
i += 1;
}
try expect(i == 10);
}
{#code_end#}
<p>
Use {#syntax#}continue{#endsyntax#} to jump back to the beginning of the loop.
</p>
{#code_begin|test|test_while_continue#}
const expect = @import("std").testing.expect;
{#code|test_while_continue.zig#}
test "while continue" {
var i: usize = 0;
while (true) {
i += 1;
if (i < 10)
continue;
break;
}
try expect(i == 10);
}
{#code_end#}
<p>
While loops support a continue expression which is executed when the loop
is continued. The {#syntax#}continue{#endsyntax#} keyword respects this expression.
</p>
{#code_begin|test|test_while_continue_expression#}
const expect = @import("std").testing.expect;
{#code|test_while_continue_expression.zig#}
test "while loop continue expression" {
var i: usize = 0;
while (i < 10) : (i += 1) {}
try expect(i == 10);
}
test "while loop continue expression, more complicated" {
var i: usize = 1;
var j: usize = 1;
while (i * j < 2000) : ({ i *= 2; j *= 3; }) {
const my_ij = i * j;
try expect(my_ij < 2000);
}
}
{#code_end#}
<p>
While loops are expressions. The result of the expression is the
result of the {#syntax#}else{#endsyntax#} clause of a while loop, which is executed when
@@ -4498,44 +2551,13 @@ test "while loop continue expression, more complicated" {
When you {#syntax#}break{#endsyntax#} from a while loop, the {#syntax#}else{#endsyntax#} branch is not
evaluated.
</p>
{#code_begin|test|test_while_else#}
const expect = @import("std").testing.expect;
{#code|test_while_else.zig#}
test "while else" {
try expect(rangeHasNumber(0, 10, 5));
try expect(!rangeHasNumber(0, 10, 15));
}
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;
}
{#code_end#}
{#header_open|Labeled while#}
<p>When a {#syntax#}while{#endsyntax#} loop is labeled, it can be referenced from a {#syntax#}break{#endsyntax#}
or {#syntax#}continue{#endsyntax#} from within a nested loop:</p>
{#code_begin|test|test_while_nested_break#}
test "nested break" {
outer: while (true) {
while (true) {
break :outer;
}
}
}
{#code|test_while_nested_break.zig#}
test "nested continue" {
var i: usize = 0;
outer: while (i < 10) : (i += 1) {
while (true) {
continue :outer;
}
}
}
{#code_end#}
{#header_close#}
{#header_open|while with Optionals#}
<p>
@@ -4551,45 +2573,8 @@ test "nested continue" {
The {#syntax#}else{#endsyntax#} branch is allowed on optional iteration. In this case, it will
be executed on the first null value encountered.
</p>
{#code_begin|test|test_while_null_capture#}
const expect = @import("std").testing.expect;
{#code|test_while_null_capture.zig#}
test "while null capture" {
var sum1: u32 = 0;
numbers_left = 3;
while (eventuallyNullSequence()) |value| {
sum1 += value;
}
try expect(sum1 == 3);
// null capture with an else block
var sum2: u32 = 0;
numbers_left = 3;
while (eventuallyNullSequence()) |value| {
sum2 += value;
} else {
try expect(sum2 == 3);
}
// null capture with a continue expression
var i: u32 = 0;
var sum3: u32 = 0;
numbers_left = 3;
while (eventuallyNullSequence()) |value| : (i += 1) {
sum3 += value;
}
try expect(i == 3);
}
var numbers_left: u32 = undefined;
fn eventuallyNullSequence() ?u32 {
return if (numbers_left == 0) null else blk: {
numbers_left -= 1;
break :blk numbers_left;
};
}
{#code_end#}
{#header_close#}
{#header_open|while with Error Unions#}
@@ -4603,28 +2588,8 @@ fn eventuallyNullSequence() ?u32 {
When the {#syntax#}else |x|{#endsyntax#} syntax is present on a {#syntax#}while{#endsyntax#} expression,
the while condition must have an {#link|Error Union Type#}.
</p>
{#code_begin|test|test_while_error_capture#}
const expect = @import("std").testing.expect;
{#code|test_while_error_capture.zig#}
test "while error union capture" {
var sum1: u32 = 0;
numbers_left = 3;
while (eventuallyErrorSequence()) |value| {
sum1 += value;
} else |err| {
try expect(err == error.ReachedZero);
}
}
var numbers_left: u32 = undefined;
fn eventuallyErrorSequence() anyerror!u32 {
return if (numbers_left == 0) error.ReachedZero else blk: {
numbers_left -= 1;
break :blk numbers_left;
};
}
{#code_end#}
{#header_close#}
{#header_open|inline while#}
@@ -4633,28 +2598,8 @@ fn eventuallyErrorSequence() anyerror!u32 {
allows the code to do some things which only work at compile time,
such as use types as first class values.
</p>
{#code_begin|test|test_inline_while#}
const expect = @import("std").testing.expect;
{#code|test_inline_while.zig#}
test "inline while loop" {
comptime var i = 0;
var sum: usize = 0;
inline while (i < 3) : (i += 1) {
const T = switch (i) {
0 => f32,
1 => i8,
2 => bool,
else => unreachable,
};
sum += typeNameLength(T);
}
try expect(sum == 9);
}
fn typeNameLength(comptime T: type) usize {
return @typeName(T).len;
}
{#code_end#}
<p>
It is recommended to use {#syntax#}inline{#endsyntax#} loops only for one of these reasons:
</p>
@@ -4668,124 +2613,13 @@ fn typeNameLength(comptime T: type) usize {
{#see_also|if|Optionals|Errors|comptime|unreachable#}
{#header_close#}
{#header_open|for#}
{#code_begin|test|test_for#}
const expect = @import("std").testing.expect;
{#code|test_for.zig#}
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);
}
{#code_end#}
{#header_open|Labeled for#}
<p>When a {#syntax#}for{#endsyntax#} loop is labeled, it can be referenced from a {#syntax#}break{#endsyntax#}
or {#syntax#}continue{#endsyntax#} from within a nested loop:</p>
{#code_begin|test|test_for_nested_break#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_for_nested_break.zig#}
test "nested break" {
var count: usize = 0;
outer: for (1..6) |_| {
for (1..6) |_| {
count += 1;
break :outer;
}
}
try expect(count == 1);
}
test "nested continue" {
var count: usize = 0;
outer: for (1..9) |_| {
for (1..6) |_| {
count += 1;
continue :outer;
}
}
try expect(count == 8);
}
{#code_end#}
{#header_close#}
{#header_open|inline for#}
<p>
@@ -4795,28 +2629,8 @@ test "nested continue" {
The capture value and iterator value of inlined for loops are
compile-time known.
</p>
{#code_begin|test|test_inline_for#}
const expect = @import("std").testing.expect;
{#code|test_inline_for.zig#}
test "inline for loop" {
const nums = [_]i32{2, 4, 6};
var sum: usize = 0;
inline for (nums) |i| {
const T = switch (i) {
2 => f32,
4 => i8,
6 => bool,
else => unreachable,
};
sum += typeNameLength(T);
}
try expect(sum == 9);
}
fn typeNameLength(comptime T: type) usize {
return @typeName(T).len;
}
{#code_end#}
<p>
It is recommended to use {#syntax#}inline{#endsyntax#} loops only for one of these reasons:
</p>
@@ -4830,229 +2644,25 @@ fn typeNameLength(comptime T: type) usize {
{#see_also|while|comptime|Arrays|Slices#}
{#header_close#}
{#header_open|if#}
{#code_begin|test|test_if#}
// If expressions have three uses, corresponding to the three types:
// * bool
// * ?T
// * anyerror!T
{#code|test_if.zig#}
const expect = @import("std").testing.expect;
test "if expression" {
// If expressions are used instead of a ternary expression.
const a: u32 = 5;
const b: u32 = 4;
const result = if (a != b) 47 else 3089;
try expect(result == 47);
}
test "if boolean" {
// If expressions test boolean conditions.
const a: u32 = 5;
const b: u32 = 4;
if (a != b) {
try expect(true);
} else if (a == 9) {
unreachable;
} else {
unreachable;
}
}
test "if error union" {
// If expressions test for errors.
// Note the |err| capture on the else.
const a: anyerror!u32 = 0;
if (a) |value| {
try expect(value == 0);
} else |err| {
_ = err;
unreachable;
}
const b: anyerror!u32 = error.BadValue;
if (b) |value| {
_ = value;
unreachable;
} else |err| {
try expect(err == error.BadValue);
}
// The else and |err| capture is strictly required.
if (a) |value| {
try expect(value == 0);
} else |_| {}
// To check only the error value, use an empty block expression.
if (b) |_| {} else |err| {
try expect(err == error.BadValue);
}
// Access the value by reference using a pointer capture.
var c: anyerror!u32 = 3;
if (c) |*value| {
value.* = 9;
} else |_| {
unreachable;
}
if (c) |value| {
try expect(value == 9);
} else |_| {
unreachable;
}
}
{#code_end#}
{#header_open|if with Optionals#}
{#code_begin|test|test_if_optionals#}
const expect = @import("std").testing.expect;
{#code|test_if_optionals.zig#}
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;
}
}
{#code_end#}
{#header_close#}
{#see_also|Optionals|Errors#}
{#header_close#}
{#header_open|defer#}
<p>Executes an expression unconditionally at scope exit.</p>
{#code_begin|test|test_defer#}
const std = @import("std");
const expect = std.testing.expect;
const print = std.debug.print;
{#code|test_defer.zig#}
fn deferExample() !usize {
var a: usize = 1;
{
defer a = 2;
a = 1;
}
try expect(a == 2);
a = 5;
return a;
}
test "defer basics" {
try expect((try deferExample()) == 5);
}
{#code_end#}
<p>Defer expressions are evaluated in reverse order.</p>
{#code_begin|test|defer_unwind#}
const std = @import("std");
const expect = std.testing.expect;
const print = std.debug.print;
{#code|defer_unwind.zig#}
test "defer unwinding" {
print("\n", .{});
defer {
print("1 ", .{});
}
defer {
print("2 ", .{});
}
if (false) {
// defers are not run if they are never executed.
defer {
print("3 ", .{});
}
}
}
{#code_end#}
<p>Inside a defer expression the return statement is not allowed.</p>
{#code_begin|test_err|test_invalid_defer|cannot return from defer expression#}
fn deferInvalidExample() !void {
defer {
return error.DeferError;
}
{#code|test_invalid_defer.zig#}
return error.DeferError;
}
{#code_end#}
{#see_also|Errors#}
{#header_close#}
{#header_open|unreachable#}
@@ -5065,45 +2675,15 @@ fn deferInvalidExample() !void {
will never be hit to perform optimizations.
</p>
{#header_open|Basics#}
{#code_begin|test|test_unreachable#}
// 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;
}
}
{#code_end#}
<p>In fact, this is how {#syntax#}std.debug.assert{#endsyntax#} is implemented:</p>
{#code_begin|test_err|test_assertion_failure#}
// This is how std.debug.assert is implemented
fn assert(ok: bool) void {
if (!ok) unreachable; // assertion failure
}
{#code|test_unreachable.zig#}
<p>In fact, this is how {#syntax#}std.debug.assert{#endsyntax#} is implemented:</p>
{#code|test_assertion_failure.zig#}
// This test will fail because we hit unreachable.
test "this will fail" {
assert(false);
}
{#code_end#}
{#header_close#}
{#header_open|At Compile-Time#}
{#code_begin|test_err|test_comptime_unreachable|unreachable code#}
const assert = @import("std").debug.assert;
{#code|test_comptime_unreachable.zig#}
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);
}
}
{#code_end#}
{#see_also|Zig Test|Build Mode|comptime#}
{#header_close#}
{#header_close#}
@@ -5121,101 +2701,16 @@ test "type of unreachable" {
<p>When resolving types together, such as {#syntax#}if{#endsyntax#} clauses or {#syntax#}switch{#endsyntax#} prongs,
the {#syntax#}noreturn{#endsyntax#} type is compatible with every other type. Consider:
</p>
{#code_begin|test|test_noreturn#}
fn foo(condition: bool, b: u32) void {
const a = if (condition) b else return;
_ = a;
@panic("do something with a");
}
test "noreturn" {
foo(false, 1);
}
{#code_end#}
{#code|test_noreturn.zig#}
<p>Another use case for {#syntax#}noreturn{#endsyntax#} is the {#syntax#}exit{#endsyntax#} function:</p>
{#code_begin|test|test_noreturn_from_exit#}
{#target_windows#}
const std = @import("std");
const builtin = @import("builtin");
const native_arch = builtin.cpu.arch;
const expect = std.testing.expect;
{#code|test_noreturn_from_exit.zig#}
const WINAPI: std.builtin.CallingConvention = if (native_arch == .x86) .Stdcall else .C;
extern "kernel32" fn ExitProcess(exit_code: c_uint) callconv(WINAPI) noreturn;
test "foo" {
const value = bar() catch ExitProcess(1);
try expect(value == 1234);
}
fn bar() anyerror!u32 {
return 1234;
}
{#code_end#}
{#header_close#}
{#header_open|Functions#}
{#code_begin|test|test_functions#}
const std = @import("std");
const builtin = @import("builtin");
const native_arch = builtin.cpu.arch;
const expect = std.testing.expect;
{#code|test_functions.zig#}
// Functions are declared like this
fn add(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.
export fn sub(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" fn ExitProcess(exit_code: u32) callconv(WINAPI) noreturn;
extern "c" fn atan2(a: f64, b: f64) f64;
// The @setCold builtin tells the optimizer that a function is rarely called.
fn abort() noreturn {
@setCold(true);
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.
fn shiftLeftOne(a: u32) callconv(.Inline) u32 {
return a << 1;
}
// The pub specifier allows the function to be visible when importing.
// Another file can use @import and call sub2
pub fn sub2(a: i8, b: i8) i8 { return a - b; }
// Function pointers are prefixed with `*const `.
const Call2Op = *const fn (a: i8, b: i8) i8;
fn doOp(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);
}
{#code_end#}
<p>There is a difference between a function <em>body</em> and a function <em>pointer</em>.
Function bodies are {#link|comptime#}-only types while function {#link|Pointers#} may be
runtime-known.</p>
@@ -5232,26 +2727,8 @@ test "function" {
as parameters, Zig may choose to copy and pass by value, or pass by reference, whichever way
Zig decides will be faster. This is made possible, in part, by the fact that parameters are immutable.
</p>
{#code_begin|test|test_pass_by_reference_or_value#}
const Point = struct {
x: i32,
y: i32,
};
{#code|test_pass_by_reference_or_value.zig#}
fn foo(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);
}
{#code_end#}
<p>
For extern functions, Zig follows the C ABI for passing structs and unions by value.
</p>
@@ -5262,21 +2739,8 @@ test "pass struct to function" {
In this case the parameter types will be inferred when the function is called.
Use {#link|@TypeOf#} and {#link|@typeInfo#} to get information about the inferred type.
</p>
{#code_begin|test|test_fn_type_inference#}
const expect = @import("std").testing.expect;
{#code|test_fn_type_inference.zig#}
fn addFortyTwo(x: anytype) @TypeOf(x) {
return x + 42;
}
test "fn type inference" {
try expect(addFortyTwo(1) == 43);
try expect(@TypeOf(addFortyTwo(1)) == comptime_int);
const y: i64 = 2;
try expect(addFortyTwo(y) == 44);
try expect(@TypeOf(addFortyTwo(y)) == i64);
}
{#code_end#}
{#header_close#}
@@ -5292,17 +2756,8 @@ test "fn type inference" {
compile-time known are treated as {#link|Compile Time Parameters#}. This can potentially
propagate all the way to the return value:
</p>
{#code_begin|test|inline_call#}
test "inline function call" {
if (foo(1200, 34) != 1234) {
@compileError("bad");
}
}
{#code|inline_call.zig#}
inline fn foo(a: i32, b: i32) i32 {
return a + b;
}
{#code_end#}
<p>If {#syntax#}inline{#endsyntax#} is removed, the test fails with the compile error
instead of passing.</p>
<p>It is generally better to let the compiler decide when to inline a
@@ -5318,18 +2773,8 @@ inline fn foo(a: i32, b: i32) i32 {
{#header_close#}
{#header_open|Function Reflection#}
{#code_begin|test|test_fn_reflection#}
const std = @import("std");
const math = std.math;
const testing = std.testing;
{#code|test_fn_reflection.zig#}
test "fn reflection" {
try testing.expect(@typeInfo(@TypeOf(testing.expect)).Fn.params[0].type.? == bool);
try testing.expect(@typeInfo(@TypeOf(testing.tmpDir)).Fn.return_type.? == testing.TmpDir);
try testing.expect(@typeInfo(@TypeOf(math.Log2Int)).Fn.is_generic);
}
{#code_end#}
{#header_close#}
{#header_close#}
{#header_open|Errors#}
@@ -5348,60 +2793,21 @@ test "fn reflection" {
<p>
You can {#link|coerce|Type Coercion#} an error from a subset to a superset:
</p>
{#code_begin|test|test_coerce_error_subset_to_superset#}
const std = @import("std");
{#code|test_coerce_error_subset_to_superset.zig#}
const FileOpenError = error {
AccessDenied,
OutOfMemory,
FileNotFound,
};
const AllocationError = error {
OutOfMemory,
};
test "coerce subset to superset" {
const err = foo(AllocationError.OutOfMemory);
try std.testing.expect(err == FileOpenError.OutOfMemory);
}
fn foo(err: AllocationError) FileOpenError {
return err;
}
{#code_end#}
<p>
But you cannot {#link|coerce|Type Coercion#} an error from a superset to a subset:
</p>
{#code_begin|test_err|test_coerce_error_superset_to_subset|not a member of destination error set#}
const FileOpenError = error {
AccessDenied,
OutOfMemory,
FileNotFound,
};
{#code|test_coerce_error_superset_to_subset.zig#}
const AllocationError = error {
OutOfMemory,
};
test "coerce superset to subset" {
foo(FileOpenError.OutOfMemory) catch {};
}
fn foo(err: FileOpenError) AllocationError {
return err;
}
{#code_end#}
<p>
There is a shortcut for declaring an error set with only 1 value, and then getting that value:
</p>
{#code_begin|syntax|single_value_error_set_shortcut#}
const err = error.FileNotFound;
{#code_end#}
{#code|single_value_error_set_shortcut.zig#}
<p>This is equivalent to:</p>
{#code_begin|syntax|single_value_error_set#}
const err = (error {FileNotFound}).FileNotFound;
{#code_end#}
{#code|single_value_error_set.zig#}
<p>
This becomes useful when using {#link|Inferred Error Sets#}.
</p>
@@ -5432,47 +2838,8 @@ const err = (error {FileNotFound}).FileNotFound;
<p>
Here is a function to parse a string into a 64-bit integer:
</p>
{#code_begin|test|error_union_parsing_u64#}
const std = @import("std");
const maxInt = std.math.maxInt;
{#code|error_union_parsing_u64.zig#}
pub fn parseU64(buf: []const u8, radix: u8) !u64 {
var x: u64 = 0;
for (buf) |c| {
const digit = charToDigit(c);
if (digit >= radix) {
return error.InvalidChar;
}
// x *= radix
var ov = @mulWithOverflow(x, radix);
if (ov[1] != 0) return error.OverFlow;
// x += digit
ov = @addWithOverflow(ov[0], digit);
if (ov[1] != 0) return error.OverFlow;
x = ov[0];
}
return x;
}
fn charToDigit(c: u8) u8 {
return switch (c) {
'0' ... '9' => c - '0',
'A' ... 'Z' => c - 'A' + 10,
'a' ... 'z' => c - 'a' + 10,
else => maxInt(u8),
};
}
test "parse u64" {
const result = try parseU64("1234", 10);
try std.testing.expect(result == 1234);
}
{#code_end#}
<p>
Notice the return type is {#syntax#}!u64{#endsyntax#}. This means that the function
either returns an unsigned 64 bit integer, or an error. We left off the error set
@@ -5495,14 +2862,8 @@ test "parse u64" {
</ul>
{#header_open|catch#}
<p>If you want to provide a default value, you can use the {#syntax#}catch{#endsyntax#} binary operator:</p>
{#code_begin|syntax|catch#}
const parseU64 = @import("error_union_parsing_u64.zig").parseU64;
{#code|catch.zig#}
fn doAThing(str: []u8) void {
const number = parseU64(str, 10) catch 13;
_ = number; // ...
}
{#code_end#}
<p>
In this code, {#syntax#}number{#endsyntax#} will be equal to the successfully parsed string, or
a default value of 13. The type of the right hand side of the binary {#syntax#}catch{#endsyntax#} operator must
@@ -5513,40 +2874,19 @@ fn doAThing(str: []u8) void {
{#syntax#}catch{#endsyntax#} after performing some logic, you
can combine {#syntax#}catch{#endsyntax#} with named {#link|Blocks#}:
</p>
{#code_begin|syntax|handle_error_with_catch_block.zig#}
const parseU64 = @import("error_union_parsing_u64.zig").parseU64;
{#code|handle_error_with_catch_block.zig.zig#}
fn doAThing(str: []u8) void {
const number = parseU64(str, 10) catch blk: {
// do things
break :blk 13;
};
_ = number; // number is now initialized
}
{#code_end#}
{#header_close#}
{#header_open|try#}
<p>Let's say you wanted to return the error if you got one, otherwise continue with the
function logic:</p>
{#code_begin|syntax|catch_err_return#}
const parseU64 = @import("error_union_parsing_u64.zig").parseU64;
{#code|catch_err_return.zig#}
fn doAThing(str: []u8) !void {
const number = parseU64(str, 10) catch |err| return err;
_ = number; // ...
}
{#code_end#}
<p>
There is a shortcut for this. The {#syntax#}try{#endsyntax#} expression:
</p>
{#code_begin|syntax|try#}
const parseU64 = @import("error_union_parsing_u64.zig").parseU64;
{#code|try.zig#}
fn doAThing(str: []u8) !void {
const number = try parseU64(str, 10);
_ = number; // ...
}
{#code_end#}
<p>
{#syntax#}try{#endsyntax#} evaluates an error union expression. If it is an error, it returns
from the current function with the same error. Otherwise, the expression results in
@@ -5653,174 +2993,26 @@ fn createFoo(param: i32) !Foo {
It should be noted that {#syntax#}errdefer{#endsyntax#} statements only last until the end of the block
they are written in, and therefore are not run if an error is returned outside of that block:
</p>
{#code_begin|test_err|test_errdefer_slip_ups|1 tests leaked memory#}
const std = @import("std");
const Allocator = std.mem.Allocator;
{#code|test_errdefer_slip_ups.zig#}
const Foo = struct {
data: u32,
};
fn tryToAllocateFoo(allocator: Allocator) !*Foo {
return allocator.create(Foo);
}
fn deallocateFoo(allocator: Allocator, foo: *Foo) void {
allocator.destroy(foo);
}
fn getFooData() !u32 {
return 666;
}
fn createFoo(allocator: Allocator, param: i32) !*Foo {
const foo = getFoo: {
var foo = try tryToAllocateFoo(allocator);
errdefer deallocateFoo(allocator, foo); // Only lasts until the end of getFoo
// Calls deallocateFoo on error
foo.data = try getFooData();
break :getFoo foo;
};
// Outside of the scope of the errdefer, so
// deallocateFoo will not be called here
if (param > 1337) return error.InvalidParam;
return foo;
}
test "createFoo" {
try std.testing.expectError(error.InvalidParam, createFoo(std.testing.allocator, 2468));
}
{#code_end#}
<p>
To ensure that {#syntax#}deallocateFoo{#endsyntax#} is properly called
when returning an error, you must add an {#syntax#}errdefer{#endsyntax#} outside of the block:
</p>
{#code_begin|test|test_errdefer_block#}
const std = @import("std");
const Allocator = std.mem.Allocator;
{#code|test_errdefer_block.zig#}
const Foo = struct {
data: u32,
};
fn tryToAllocateFoo(allocator: Allocator) !*Foo {
return allocator.create(Foo);
}
fn deallocateFoo(allocator: Allocator, foo: *Foo) void {
allocator.destroy(foo);
}
fn getFooData() !u32 {
return 666;
}
fn createFoo(allocator: Allocator, param: i32) !*Foo {
const foo = getFoo: {
var foo = try tryToAllocateFoo(allocator);
errdefer deallocateFoo(allocator, foo);
foo.data = try getFooData();
break :getFoo foo;
};
// This lasts for the rest of the function
errdefer deallocateFoo(allocator, foo);
// Error is now properly handled by errdefer
if (param > 1337) return error.InvalidParam;
return foo;
}
test "createFoo" {
try std.testing.expectError(error.InvalidParam, createFoo(std.testing.allocator, 2468));
}
{#code_end#}
<p>
The fact that errdefers only last for the block they are declared in is
especially important when using loops:
</p>
{#code_begin|test_err|test_errdefer_loop_leak|3 errors were logged#}
const std = @import("std");
const Allocator = std.mem.Allocator;
{#code|test_errdefer_loop_leak.zig#}
const Foo = struct {
data: *u32
};
fn getData() !u32 {
return 666;
}
fn genFoos(allocator: Allocator, num: usize) ![]Foo {
const foos = try allocator.alloc(Foo, num);
errdefer allocator.free(foos);
for (foos, 0..) |*foo, i| {
foo.data = try allocator.create(u32);
// This errdefer does not last between iterations
errdefer allocator.destroy(foo.data);
// The data for the first 3 foos will be leaked
if(i >= 3) return error.TooManyFoos;
foo.data.* = try getData();
}
return foos;
}
test "genFoos" {
try std.testing.expectError(error.TooManyFoos, genFoos(std.testing.allocator, 5));
}
{#code_end#}
<p>
Special care must be taken with code that allocates in a loop
to make sure that no memory is leaked when returning an error:
</p>
{#code_begin|test|test_errdefer_loop#}
const std = @import("std");
const Allocator = std.mem.Allocator;
{#code|test_errdefer_loop.zig#}
const Foo = struct {
data: *u32
};
fn getData() !u32 {
return 666;
}
fn genFoos(allocator: Allocator, num: usize) ![]Foo {
const foos = try allocator.alloc(Foo, num);
errdefer allocator.free(foos);
// Used to track how many foos have been initialized
// (including their data being allocated)
var num_allocated: usize = 0;
errdefer for (foos[0..num_allocated]) |foo| {
allocator.destroy(foo.data);
};
for (foos, 0..) |*foo, i| {
foo.data = try allocator.create(u32);
num_allocated += 1;
if (i >= 3) return error.TooManyFoos;
foo.data.* = try getData();
}
return foos;
}
test "genFoos" {
try std.testing.expectError(error.TooManyFoos, genFoos(std.testing.allocator, 5));
}
{#code_end#}
{#header_close#}
<p>
A couple of other tidbits about error handling:
@@ -5841,25 +3033,8 @@ test "genFoos" {
<p>An error union is created with the {#syntax#}!{#endsyntax#} binary operator.
You can use compile-time reflection to access the child type of an error union:</p>
{#code_begin|test|test_error_union#}
const expect = @import("std").testing.expect;
{#code|test_error_union.zig#}
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:
try comptime expect(@typeInfo(@TypeOf(foo)).ErrorUnion.payload == i32);
// Use compile-time reflection to access the error set type of an error union:
try comptime expect(@typeInfo(@TypeOf(foo)).ErrorUnion.error_set == anyerror);
}
{#code_end#}
{#header_open|Merging Error Sets#}
<p>
Use the {#syntax#}||{#endsyntax#} operator to merge two error sets together. The resulting
@@ -5873,69 +3048,16 @@ test "error union" {
{#syntax#}LinuxFileOpenError || WindowsFileOpenError{#endsyntax#} for the error set of opening
files.
</p>
{#code_begin|test|test_merging_error_sets#}
const A = error{
NotDir,
{#code|test_merging_error_sets.zig#}
/// A doc comment
PathNotFound,
};
const B = error{
OutOfMemory,
/// B doc comment
PathNotFound,
};
const C = A || B;
fn foo() C!void {
return error.NotDir;
}
test "merge error sets" {
if (foo()) {
@panic("unexpected");
} else |err| switch (err) {
error.OutOfMemory => @panic("unexpected"),
error.PathNotFound => @panic("unexpected"),
error.NotDir => {},
}
}
{#code_end#}
{#header_close#}
{#header_open|Inferred Error Sets#}
<p>
Because many functions in Zig return a possible error, Zig supports inferring the error set.
To infer the error set for a function, prepend the {#syntax#}!{#endsyntax#} operator to the function’s return type, like {#syntax#}!T{#endsyntax#}:
</p>
{#code_begin|test|test_inferred_error_sets#}
// With an inferred error set
pub fn add_inferred(comptime T: type, a: T, b: T) !T {
const ov = @addWithOverflow(a, b);
if (ov[1] != 0) return error.Overflow;
return ov[0];
}
{#code|test_inferred_error_sets.zig#}
// With an explicit error set
pub fn add_explicit(comptime T: type, a: T, b: T) Error!T {
const ov = @addWithOverflow(a, b);
if (ov[1] != 0) return error.Overflow;
return ov[0];
}
const Error = error {
Overflow,
};
const std = @import("std");
test "inferred error set" {
if (add_inferred(u8, 255, 1)) |_| unreachable else |err| switch (err) {
error.Overflow => {}, // ok
}
}
{#code_end#}
<p>
When a function has an inferred error set, that function becomes generic and thus it becomes
trickier to do certain things with it, such as obtain a function pointer, or have an error
@@ -5955,47 +3077,8 @@ test "inferred error set" {
<p>
Error Return Traces show all the points in the code that an error was returned to the calling function. This makes it practical to use {#link|try#} everywhere and then still be able to know what happened if an error ends up bubbling all the way out of your application.
</p>
{#code_begin|exe_err|error_return_trace#}
pub fn main() !void {
try foo(12);
}
{#code|error_return_trace.zig#}
fn foo(x: i32) !void {
if (x >= 5) {
try bar();
} else {
try bang2();
}
}
fn bar() !void {
if (baz()) {
try quux();
} else |err| switch (err) {
error.FileNotFound => try hello(),
}
}
fn baz() !void {
try bang1();
}
fn quux() !void {
try bang2();
}
fn hello() !void {
try bang2();
}
fn bang1() !void {
return error.FileNotFound;
}
fn bang2() !void {
return error.PermissionDenied;
}
{#code_end#}
<p>
Look closely at this example. This is no stack trace.
</p>
@@ -6004,47 +3087,8 @@ fn bang2() !void {
but the original error that started this whole thing was {#syntax#}FileNotFound{#endsyntax#}. In the {#syntax#}bar{#endsyntax#} function, the code handles the original error code,
and then returns another one, from the switch statement. Error Return Traces make this clear, whereas a stack trace would look like this:
</p>
{#code_begin|exe_err|stack_trace#}
pub fn main() void {
foo(12);
}
{#code|stack_trace.zig#}
fn foo(x: i32) void {
if (x >= 5) {
bar();
} else {
bang2();
}
}
fn bar() void {
if (baz()) {
quux();
} else {
hello();
}
}
fn baz() bool {
return bang1();
}
fn quux() void {
bang2();
}
fn hello() void {
bang2();
}
fn bang1() bool {
return false;
}
fn bang2() void {
@panic("PermissionDenied");
}
{#code_end#}
<p>
Here, the stack trace does not explain how the control
flow in {#syntax#}bar{#endsyntax#} got to the {#syntax#}hello(){#endsyntax#} call.
@@ -6126,13 +3170,8 @@ fn __zig_return_error(stack_trace: *StackTrace) void {
The question mark symbolizes the optional type. You can convert a type to an optional
type by putting a question mark in front of it, like this:
</p>
{#code_begin|syntax|optional_integer#}
// normal integer
const normal_int: i32 = 1234;
{#code|optional_integer.zig#}
// optional integer
const optional_int: ?i32 = 5678;
{#code_end#}
<p>
Now the variable {#syntax#}optional_int{#endsyntax#} could be an {#syntax#}i32{#endsyntax#}, or {#syntax#}null{#endsyntax#}.
</p>
@@ -6198,20 +3237,8 @@ void do_a_thing(struct Foo *foo) {
<p>
In Zig you can accomplish the same thing:
</p>
{#code_begin|syntax|checking_null_in_zig#}
const Foo = struct{};
fn doSomethingWithFoo(foo: *Foo) void { _ = foo; }
{#code|checking_null_in_zig.zig#}
fn doAThing(optional_foo: ?*Foo) void {
// do some stuff
if (optional_foo) |foo| {
doSomethingWithFoo(foo);
}
// do some stuff
}
{#code_end#}
<p>
Once again, the notable thing here is that inside the if block,
{#syntax#}foo{#endsyntax#} is no longer an optional pointer, it is a pointer, which
@@ -6227,51 +3254,22 @@ fn doAThing(optional_foo: ?*Foo) void {
{#header_open|Optional Type#}
<p>An optional is created by putting {#syntax#}?{#endsyntax#} in front of a type. You can use compile-time
reflection to access the child type of an optional:</p>
{#code_begin|test|test_optional_type#}
const expect = @import("std").testing.expect;
{#code|test_optional_type.zig#}
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:
try comptime expect(@typeInfo(@TypeOf(foo)).Optional.child == i32);
}
{#code_end#}
{#header_close#}
{#header_open|null#}
<p>
Just like {#link|undefined#}, {#syntax#}null{#endsyntax#} has its own type, and the only way to use it is to
cast it to a different type:
</p>
{#code_begin|syntax|null#}
const optional_value: ?i32 = null;
{#code_end#}
{#code|null.zig#}
{#header_close#}
{#header_open|Optional Pointers#}
<p>An optional pointer is guaranteed to be the same size as a pointer. The {#syntax#}null{#endsyntax#} of
the optional is guaranteed to be address 0.</p>
{#code_begin|test|test_optional_pointer#}
const expect = @import("std").testing.expect;
{#code|test_optional_pointer.zig#}
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));
}
{#code_end#}
{#header_close#}
{#see_also|while with Optionals|if with Optionals#}
@@ -6288,28 +3286,8 @@ test "optional pointers" {
<p>
Type coercion occurs when one type is expected, but different type is provided:
</p>
{#code_begin|test|test_type_coercion#}
test "type coercion - variable declaration" {
const a: u8 = 1;
const b: u16 = a;
_ = b;
}
{#code|test_type_coercion.zig#}
test "type coercion - function call" {
const a: u8 = 1;
foo(a);
}
fn foo(b: u16) void {
_ = b;
}
test "type coercion - @as builtin" {
const a: u8 = 1;
const b = @as(u16, a);
_ = b;
}
{#code_end#}
<p>
Type coercions are only allowed when it is completely unambiguous how to get from one type to another,
and the transformation is guaranteed to be safe. There is one exception, which is {#link|C Pointers#}.
@@ -6328,65 +3306,21 @@ test "type coercion - @as builtin" {
<p>
These casts are no-ops at runtime since the value representation does not change.
</p>
{#code_begin|test|test_no_op_casts#}
test "type coercion - const qualification" {
var a: i32 = 1;
const b: *i32 = &a;
foo(b);
}
{#code|test_no_op_casts.zig#}
fn foo(_: *const i32) void {}
{#code_end#}
<p>
In addition, pointers coerce to const optional pointers:
</p>
{#code_begin|test|test_pointer_coerce_const_optional#}
const std = @import("std");
const expect = std.testing.expect;
const mem = std.mem;
{#code|test_pointer_coerce_const_optional.zig#}
test "cast *[1][*]const u8 to [*]const ?[*]const u8" {
const window_name = [1][*]const u8{"window name"};
const x: [*]const ?[*]const u8 = &window_name;
try expect(mem.eql(u8, std.mem.sliceTo(@as([*:0]const u8, @ptrCast(x[0].?)), 0), "window name"));
}
{#code_end#}
{#header_close#}
{#header_open|Type Coercion: Integer and Float Widening#}
<p>
{#link|Integers#} coerce to integer types which can represent every value of the old type, and likewise
{#link|Floats#} coerce to float types which can represent every value of the old type.
</p>
{#code_begin|test|test_integer_widening#}
const std = @import("std");
const builtin = @import("builtin");
const expect = std.testing.expect;
const mem = std.mem;
{#code|test_integer_widening.zig#}
test "integer widening" {
const a: u8 = 250;
const b: u16 = a;
const c: u32 = b;
const d: u64 = c;
const e: u64 = d;
const f: u128 = e;
try expect(f == a);
}
test "implicit unsigned integer to signed integer" {
const a: u8 = 250;
const b: i16 = a;
try expect(b == 250);
}
test "float widening" {
const a: f16 = 12.34;
const b: f32 = a;
const c: f64 = b;
const d: f128 = c;
try expect(d == a);
}
{#code_end#}
{#header_close#}
{#header_open|Type Coercion: Float to Int#}
<p>
@@ -6397,203 +3331,45 @@ test "float widening" {
<li>Cast {#syntax#}54.0{#endsyntax#} to {#syntax#}comptime_int{#endsyntax#} resulting in {#syntax#}@as(comptime_int, 10){#endsyntax#}, which is casted to {#syntax#}@as(f32, 10){#endsyntax#}</li>
<li>Cast {#syntax#}5{#endsyntax#} to {#syntax#}comptime_float{#endsyntax#} resulting in {#syntax#}@as(comptime_float, 10.8){#endsyntax#}, which is casted to {#syntax#}@as(f32, 10.8){#endsyntax#}</li>
</ul>
{#code_begin|test_err|test_ambiguous_coercion#}
// Compile time coercion of float to int
test "implicit cast to comptime_int" {
const f: f32 = 54.0 / 5;
_ = f;
}
{#code_end#}
{#code|test_ambiguous_coercion.zig#}
{#header_close#}
{#header_open|Type Coercion: Slices, Arrays and Pointers#}
{#code_begin|test|test_coerce_slices_arrays_and_pointers#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_coerce_slices_arrays_and_pointers.zig#}
// 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: []const u8 = "hello";
const x2: []const u8 = &[5]u8{ 'h', 'e', 'l', 'l', 111 };
try expect(std.mem.eql(u8, x1, x2));
const y: []const f32 = &[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![]const u8 = "hello";
const x2: anyerror![]const u8 = &[5]u8{ 'h', 'e', 'l', 'l', 111 };
try expect(std.mem.eql(u8, try x1, try x2));
const y: anyerror![]const f32 = &[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: ?[]const u8 = "hello";
const x2: ?[]const u8 = &[5]u8{ 'h', 'e', 'l', 'l', 111 };
try expect(std.mem.eql(u8, x1.?, x2.?));
const y: ?[]const f32 = &[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: []const f32 = &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);
}
{#code_end#}
{#see_also|C Pointers#}
{#header_close#}
{#header_open|Type Coercion: Optionals#}
<p>
The payload type of {#link|Optionals#}, as well as {#link|null#}, coerce to the optional type.
</p>
{#code_begin|test|test_coerce_optionals#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_coerce_optionals.zig#}
test "coerce to optionals" {
const x: ?i32 = 1234;
const y: ?i32 = null;
try expect(x.? == 1234);
try expect(y == null);
}
{#code_end#}
<p>Optionals work nested inside the {#link|Error Union Type#}, too:</p>
{#code_begin|test|test_coerce_optional_wrapped_error_union#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_coerce_optional_wrapped_error_union.zig#}
test "coerce to optionals wrapped in error union" {
const x: anyerror!?i32 = 1234;
const y: anyerror!?i32 = null;
try expect((try x).? == 1234);
try expect((try y) == null);
}
{#code_end#}
{#header_close#}
{#header_open|Type Coercion: Error Unions#}
<p>The payload type of an {#link|Error Union Type#} as well as the {#link|Error Set Type#}
coerce to the error union type:
</p>
{#code_begin|test|test_coerce_to_error_union#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_coerce_to_error_union.zig#}
test "coercion to error unions" {
const x: anyerror!i32 = 1234;
const y: anyerror!i32 = error.Failure;
try expect((try x) == 1234);
try std.testing.expectError(error.Failure, y);
}
{#code_end#}
{#header_close#}
{#header_open|Type Coercion: Compile-Time Known Numbers#}
<p>When a number is {#link|comptime#}-known to be representable in the destination type,
it may be coerced:
</p>
{#code_begin|test|test_coerce_large_to_small#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_coerce_large_to_small.zig#}
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);
}
{#code_end#}
{#header_close#}
{#header_open|Type Coercion: Unions and Enums#}
<p>Tagged unions can be coerced to enums, and enums can be coerced to tagged unions
when they are {#link|comptime#}-known to be a field of the union that has only one possible value, such as
{#link|void#}:
</p>
{#code_begin|test|test_coerce_unions_enums#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_coerce_unions_enums.zig#}
const E = enum {
one,
two,
three,
};
const U = union(E) {
one: i32,
two: f32,
three,
};
const U2 = union(enum) {
a: void,
b: f32,
fn tag(self: U2) usize {
switch (self) {
.a => return 1,
.b => return 2,
}
}
};
test "coercion between unions and enums" {
const u = U{ .two = 12.34 };
const e: E = u; // coerce union to enum
try expect(e == E.two);
const three = E.three;
const u_2: U = three; // coerce enum to union
try expect(u_2 == E.three);
const u_3: U = .three; // coerce enum literal to union
try expect(u_3 == E.three);
const u_4: U2 = .a; // coerce enum literal to union with inferred enum tag type.
try expect(u_4.tag() == 1);
// The following example is invalid.
// error: coercion from enum '@TypeOf(.enum_literal)' to union 'test_coerce_unions_enum.U2' must initialize 'f32' field 'b'
//var u_5: U2 = .b;
//try expect(u_5.tag() == 2);
}
{#code_end#}
{#see_also|union|enum#}
{#header_close#}
{#header_open|Type Coercion: undefined#}
@@ -6602,17 +3378,8 @@ test "coercion between unions and enums" {
{#header_open|Type Coercion: Tuples to Arrays#}
<p>{#link|Tuples#} can be coerced to arrays, if all of the fields have the same type.</p>
{#code_begin|test|test_coerce_tuples_arrays#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_coerce_tuples_arrays.zig#}
const Tuple = struct{ u8, u8 };
test "coercion from homogenous tuple to array" {
const tuple: Tuple = .{5, 6};
const array: [2]u8 = tuple;
_ = array;
}
{#code_end#}
{#header_close#}
{#header_close#}
@@ -6657,126 +3424,8 @@ test "coercion from homogenous tuple to array" {
This kind of type resolution chooses a type that all peer types can coerce into. Here are
some examples:
</p>
{#code_begin|test|test_peer_type_resolution#}
const std = @import("std");
const expect = std.testing.expect;
const mem = std.mem;
{#code|test_peer_type_resolution.zig#}
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"));
try comptime expect(mem.eql(u8, boolToStr(true), "true"));
try comptime expect(mem.eql(u8, boolToStr(false), "false"));
}
fn boolToStr(b: bool) []const u8 {
return if (b) "true" else "false";
}
test "peer resolve array and const slice" {
try testPeerResolveArrayConstSlice(true);
try comptime testPeerResolveArrayConstSlice(true);
}
fn testPeerResolveArrayConstSlice(b: bool) !void {
const value1 = if (b) "aoeu" else @as([]const u8, "zz");
const value2 = if (b) @as([]const u8, "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);
}
}
fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize {
if (c) {
return if (b) null else @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);
}
}
fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 {
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);
}
}
fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) anyerror![]u8 {
if (a) {
return &[_]u8{};
}
return slice[0..1];
}
test "peer type resolution: *const T and ?*T" {
const a: *const usize = @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 + 3
else |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);
}
{#code_end#}
{#header_close#}
{#header_close#}
@@ -6795,14 +3444,8 @@ test "peer type resolution: error union switch" {
require 0 bits to represent. Code that makes use of these types is
not included in the final generated code:
</p>
{#code_begin|syntax|zero_bit_types#}
export fn entry() void {
var x: void = {};
var y: void = {};
x = y;
y = x;
}
{#code_end#}
{#code|zero_bit_types.zig#}
<p>When this turns into machine code, there is no code generated in the
body of {#syntax#}entry{#endsyntax#}, even in {#link|Debug#} mode. For example, on x86_64:</p>
<pre><code>0000000000000010 <entry>:
@@ -6819,24 +3462,8 @@ export fn entry() void {
{#syntax#}Map(Key, Value){#endsyntax#}, one can pass {#syntax#}void{#endsyntax#} for the {#syntax#}Value{#endsyntax#}
type to make it into a {#syntax#}Set{#endsyntax#}:
</p>
{#code_begin|test|test_void_in_hashmap#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_void_in_hashmap.zig#}
test "turn HashMap into a set with void" {
var map = std.AutoHashMap(i32, void).init(std.testing.allocator);
defer map.deinit();
try map.put(1, {});
try map.put(2, {});
try expect(map.contains(2));
try expect(!map.contains(3));
_ = map.remove(2);
try expect(!map.contains(2));
}
{#code_end#}
<p>Note that this is different from using a dummy value for the hash map value.
By using {#syntax#}void{#endsyntax#} as the type of the value, the hash map entry type has no value field, and
thus the hash map takes up less space. Further, all the code that deals with storing and loading the
@@ -6850,31 +3477,11 @@ test "turn HashMap into a set with void" {
Expressions of type {#syntax#}void{#endsyntax#} are the only ones whose value can be ignored. For example, ignoring
a non-{#syntax#}void{#endsyntax#} expression is a compile error:
</p>
{#code_begin|test_err|test_expression_ignored|ignored#}
test "ignoring expression value" {
foo();
}
{#code|test_expression_ignored.zig#}
fn foo() i32 {
return 1234;
}
{#code_end#}
<p>However, if the expression has type {#syntax#}void{#endsyntax#}, there will be no error. Expression results can be explicitly ignored by assigning them to {#syntax#}_{#endsyntax#}. </p>
{#code_begin|test|test_void_ignored#}
test "void is ignored" {
returnsVoid();
}
{#code|test_void_ignored.zig#}
test "explicitly ignoring expression value" {
_ = foo();
}
fn returnsVoid() void {}
fn foo() i32 {
return 1234;
}
{#code_end#}
{#header_close#}
{#header_close#}
@@ -6914,18 +3521,8 @@ fn foo() i32 {
<p>
We can break down the result types for each component of a simple expression as follows:
</p>
{#code_begin|test|result_type_propagation#}
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 type
try expectEqual(@as(u32, 123), s.x);
}
{#code_end#}
{#code|result_type_propagation.zig#}
<p>
This result type information is useful for the aforementioned cast builtins, as well as to avoid
the construction of pre-coercion values, and to avoid the need for explicit type coercions in some
@@ -7045,19 +3642,8 @@ test "result type propagates through struct initializer" {
expression depends on the previous value of the aggregate. The easiest way to demonstrate this is by
attempting to swap fields of a struct or array - the following logic looks sound, but in fact is not:
</p>
{#code_begin|test_err|result_location_interfering_with_swap#}
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); // succeeds
try expect(arr[1] == 1); // fails
}
{#code_end#}
{#code|result_location_interfering_with_swap.zig#}
<p>
The following table details how some common expressions propagate result locations, where
{#syntax#}x{#endsyntax#} and {#syntax#}y{#endsyntax#} are arbitrary sub-expressions. Note that
@@ -7151,14 +3737,8 @@ test "attempt to swap array elements with array initializer" {
declarations of the operand, which must be a {#link|struct#}, {#link|union#}, {#link|enum#},
or {#link|opaque#}, into the namespace:
</p>
{#code_begin|test|test_usingnamespace#}
test "using std namespace" {
const S = struct {
usingnamespace @import("std");
};
try S.testing.expect(true);
}
{#code_end#}
{#code|test_usingnamespace.zig#}
<p>
{#syntax#}usingnamespace{#endsyntax#} has an important use case when organizing the public
API of a file or package. For example, one might have <code class="file">c.zig</code> with all of the
@@ -7193,17 +3773,8 @@ pub usingnamespace @cImport({
<p>
Compile-time parameters is how Zig implements generics. It is compile-time duck typing.
</p>
{#code_begin|syntax|compile-time_duck_typing#}
fn max(comptime T: type, a: T, b: T) T {
return if (a > b) a else b;
}
fn gimmeTheBiggerFloat(a: f32, b: f32) f32 {
return max(f32, a, b);
}
fn gimmeTheBiggerInteger(a: u64, b: u64) u64 {
return max(u64, a, b);
}
{#code_end#}
{#code|compile-time_duck_typing.zig#}
<p>
In Zig, types are first-class citizens. They can be assigned to variables, passed as parameters to functions,
and returned from functions. However, they can only be used in expressions which are known at <em>compile-time</em>,
@@ -7219,21 +3790,8 @@ fn gimmeTheBiggerInteger(a: u64, b: u64) u64 {
<p>
For example, if we were to introduce another function to the above snippet:
</p>
{#code_begin|test_err|test_unresolved_comptime_value|unable to resolve comptime value#}
fn max(comptime T: type, a: T, b: T) T {
return if (a > b) a else b;
}
test "try to pass a runtime type" {
foo(false);
}
fn foo(condition: bool) void {
const result = max(
if (condition) f32 else u64,
1234,
5678);
_ = result;
}
{#code_end#}
{#code|test_unresolved_comptime_value.zig#}
<p>
This is an error because the programmer attempted to pass a value only known at run-time
to a function which expects a value known at compile-time.
@@ -7245,33 +3803,15 @@ fn foo(condition: bool) void {
<p>
For example:
</p>
{#code_begin|test_err|test_comptime_mismatched_type|operator > not allowed for type 'bool'#}
fn max(comptime T: type, a: T, b: T) T {
return if (a > b) a else b;
}
test "try to compare bools" {
_ = max(bool, true, false);
}
{#code_end#}
{#code|test_comptime_mismatched_type.zig#}
<p>
On the flip side, inside the function definition with the {#syntax#}comptime{#endsyntax#} parameter, the
value is known at compile-time. This means that we actually could make this work for the bool type
if we wanted to:
</p>
{#code_begin|test|test_comptime_max_with_bool#}
fn max(comptime T: type, a: T, b: T) T {
if (T == bool) {
return a or b;
} else if (a > b) {
return a;
} else {
return b;
}
}
test "try to compare bools" {
try @import("std").testing.expect(max(bool, false, true) == true);
}
{#code_end#}
{#code|test_comptime_max_with_bool.zig#}
<p>
This works because Zig implicitly inlines {#syntax#}if{#endsyntax#} expressions when the condition
is known at compile-time, and the compiler guarantees that it will skip analysis of
@@ -7281,13 +3821,8 @@ test "try to compare bools" {
This means that the actual function generated for {#syntax#}max{#endsyntax#} in this situation looks like
this:
</p>
{#code_begin|syntax|compiler_generated_function#}
fn max(a: bool, b: bool) bool {
{
return a or b;
}
}
{#code_end#}
{#code|compiler_generated_function.zig#}
<p>
All the code that dealt with compile-time known values is eliminated and we are left with only
the necessary run-time code to accomplish the task.
@@ -7310,40 +3845,8 @@ fn max(a: bool, b: bool) bool {
<p>
For example:
</p>
{#code_begin|test|test_comptime_evaluation#}
const expect = @import("std").testing.expect;
{#code|test_comptime_evaluation.zig#}
const CmdFn = struct {
name: []const u8,
func: fn(i32) i32,
};
const cmd_fns = [_]CmdFn{
CmdFn {.name = "one", .func = one},
CmdFn {.name = "two", .func = two},
CmdFn {.name = "three", .func = three},
};
fn one(value: i32) i32 { return value + 1; }
fn two(value: i32) i32 { return value + 2; }
fn three(value: i32) i32 { return value + 3; }
fn performFn(comptime prefix_char: u8, start_value: i32) i32 {
var result: i32 = start_value;
comptime var i = 0;
inline while (i < cmd_fns.len) : (i += 1) {
if (cmd_fns[i].name[0] == prefix_char) {
result = cmd_fns[i].func(result);
}
}
return result;
}
test "perform fn" {
try expect(performFn('t', 1) == 6);
try expect(performFn('o', 0) == 1);
try expect(performFn('w', 99) == 99);
}
{#code_end#}
<p>
This example is a bit contrived, because the compile-time evaluation component is unnecessary;
this code would work fine if it was all done at run-time. But it does end up generating
@@ -7391,15 +3894,8 @@ fn performFn(start_value: i32) i32 {
use a {#syntax#}comptime{#endsyntax#} expression to guarantee that the expression will be evaluated at compile-time.
If this cannot be accomplished, the compiler will emit an error. For example:
</p>
{#code_begin|test_err|test_comptime_call_extern_function|comptime call of extern function#}
extern fn exit() noreturn;
{#code|test_comptime_call_extern_function.zig#}
test "foo" {
comptime {
exit();
}
}
{#code_end#}
<p>
It doesn't make sense that a program could call {#syntax#}exit(){#endsyntax#} (or any other external function)
at compile-time, so this is a compile error. However, a {#syntax#}comptime{#endsyntax#} expression does much
@@ -7424,37 +3920,13 @@ test "foo" {
<p>
Let's look at an example:
</p>
{#code_begin|test|test_fibonacci_recursion#}
const expect = @import("std").testing.expect;
{#code|test_fibonacci_recursion.zig#}
fn fibonacci(index: u32) u32 {
if (index < 2) return index;
return fibonacci(index - 1) + fibonacci(index - 2);
}
test "fibonacci" {
// test fibonacci at run-time
try expect(fibonacci(7) == 13);
// test fibonacci at compile-time
try comptime expect(fibonacci(7) == 13);
}
{#code_end#}
<p>
Imagine if we had forgotten the base case of the recursive function and tried to run the tests:
</p>
{#code_begin|test_err|test_fibonacci_comptime_overflow|overflow of integer type#}
const expect = @import("std").testing.expect;
{#code|test_fibonacci_comptime_overflow.zig#}
fn fibonacci(index: u32) u32 {
//if (index < 2) return index;
return fibonacci(index - 1) + fibonacci(index - 2);
}
test "fibonacci" {
try comptime expect(fibonacci(7) == 13);
}
{#code_end#}
<p>
The compiler produces an error which is a stack trace from trying to evaluate the
function at compile-time.
@@ -7464,18 +3936,8 @@ test "fibonacci" {
undefined behavior, which is always a compile error if the compiler knows it happened.
But what would have happened if we used a signed integer?
</p>
{#code_begin|syntax|fibonacci_comptime_infinite_recursion#}
const assert = @import("std").debug.assert;
{#code|fibonacci_comptime_infinite_recursion.zig#}
fn fibonacci(index: i32) i32 {
//if (index < 2) return index;
return fibonacci(index - 1) + fibonacci(index - 2);
}
test "fibonacci" {
try comptime assert(fibonacci(7) == 13);
}
{#code_end#}
<p>
The compiler is supposed to notice that evaluating this function at
compile-time took more than 1000 branches, and thus emits an error and
@@ -7494,61 +3956,16 @@ test "fibonacci" {
What if we fix the base case, but put the wrong value in the
{#syntax#}expect{#endsyntax#} line?
</p>
{#code_begin|test_err|test_fibonacci_comptime_unreachable|reached unreachable#}
const assert = @import("std").debug.assert;
{#code|test_fibonacci_comptime_unreachable.zig#}
fn fibonacci(index: i32) i32 {
if (index < 2) return index;
return fibonacci(index - 1) + fibonacci(index - 2);
}
test "fibonacci" {
try comptime assert(fibonacci(7) == 99999);
}
{#code_end#}
<p>
At {#link|container|Containers#} level (outside of any function), all expressions are implicitly
{#syntax#}comptime{#endsyntax#} expressions. This means that we can use functions to
initialize complex static data. For example:
</p>
{#code_begin|test|test_container-level_comptime_expressions#}
const first_25_primes = firstNPrimes(25);
const sum_of_first_25_primes = sum(&first_25_primes);
{#code|test_container-level_comptime_expressions.zig#}
fn firstNPrimes(comptime n: usize) [n]i32 {
var prime_list: [n]i32 = undefined;
var next_index: usize = 0;
var test_number: i32 = 2;
while (next_index < prime_list.len) : (test_number += 1) {
var test_prime_index: usize = 0;
var is_prime = true;
while (test_prime_index < next_index) : (test_prime_index += 1) {
if (test_number % prime_list[test_prime_index] == 0) {
is_prime = false;
break;
}
}
if (is_prime) {
prime_list[next_index] = test_number;
next_index += 1;
}
}
return prime_list;
}
fn sum(numbers: []const i32) i32 {
var result: i32 = 0;
for (numbers) |x| {
result += x;
}
return result;
}
test "variable values" {
try @import("std").testing.expect(sum_of_first_25_primes == 1060);
}
{#code_end#}
<p>
When we compile this program, Zig generates the constants
with the answer pre-computed. Here are the lines from the generated LLVM IR:
@@ -7570,21 +3987,8 @@ test "variable values" {
<p>
Here is an example of a generic {#syntax#}List{#endsyntax#} data structure.
</p>
{#code_begin|syntax|generic_data_structure#}
fn List(comptime T: type) type {
return struct {
items: []T,
len: usize,
};
}
{#code|generic_data_structure.zig#}
// 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,
};
{#code_end#}
<p>
That's it. It's a function that returns an anonymous {#syntax#}struct{#endsyntax#}.
For the purposes of error messages and debugging, Zig infers the name
@@ -7594,22 +3998,8 @@ var list = List(i32){
<p>
To explicitly give a type a name, we assign it to a constant.
</p>
{#code_begin|syntax|anonymous_struct_name#}
const Node = struct {
next: ?*Node,
name: []const u8,
};
{#code|anonymous_struct_name.zig#}
var node_a = Node{
.next = null,
.name = "Node A",
};
var node_b = Node{
.next = &node_a,
.name = "Node B",
};
{#code_end#}
<p>
In this example, the {#syntax#}Node{#endsyntax#} struct refers to itself.
This works because all top level declarations are order-independent.
@@ -7622,100 +4012,15 @@ var node_b = Node{
<p>
Putting all of this together, let's see how {#syntax#}print{#endsyntax#} works in Zig.
</p>
{#code_begin|exe|print#}
const print = @import("std").debug.print;
{#code|print.zig#}
const a_number: i32 = 1234;
const a_string = "foobar";
pub fn main() void {
print("here is a string: '{s}' here is a number: {}\n", .{a_string, a_number});
}
{#code_end#}
<p>
Let's crack open the implementation of this and see how it works:
</p>
{#code_begin|syntax|poc_print_fn#}
const Writer = struct {
/// Calls print and then flushes the buffer.
pub fn print(self: *Writer, comptime format: []const u8, args: anytype) anyerror!void {
const State = enum {
start,
open_brace,
close_brace,
};
{#code|poc_print_fn.zig#}
comptime var start_index: usize = 0;
comptime var state = State.start;
comptime var next_arg: usize = 0;
inline for (format, 0..) |c, i| {
switch (state) {
State.start => switch (c) {
'{' => {
if (start_index < i) try self.write(format[start_index..i]);
state = State.open_brace;
},
'}' => {
if (start_index < i) try self.write(format[start_index..i]);
state = State.close_brace;
},
else => {},
},
State.open_brace => switch (c) {
'{' => {
state = State.start;
start_index = i;
},
'}' => {
try self.printValue(args[next_arg]);
next_arg += 1;
state = State.start;
start_index = i + 1;
},
's' => {
continue;
},
else => @compileError("Unknown format character: " ++ [1]u8{c}),
},
State.close_brace => switch (c) {
'}' => {
state = State.start;
start_index = i;
},
else => @compileError("Single '}' encountered in format string"),
},
}
}
comptime {
if (args.len != next_arg) {
@compileError("Unused arguments");
}
if (state != State.start) {
@compileError("Incomplete format string: " ++ format);
}
}
if (start_index < format.len) {
try self.write(format[start_index..format.len]);
}
try self.flush();
}
fn write(self: *Writer, value: []const u8) !void {
_ = self;
_ = value;
}
pub fn printValue(self: *Writer, value: anytype) !void {
_ = self;
_ = value;
}
fn flush(self: *Writer) !void {
_ = self;
}
};
{#code_end#}
<p>
This is a proof of concept implementation; the actual function in the standard library has more
formatting capabilities.
@@ -7741,56 +4046,13 @@ pub fn print(self: *Writer, arg0: []const u8, arg1: i32) !void {
{#syntax#}printValue{#endsyntax#} is a function that takes a parameter of any type, and does different things depending
on the type:
</p>
{#code_begin|syntax|poc_printValue_fn#}
const Writer = struct {
pub fn printValue(self: *Writer, value: anytype) !void {
switch (@typeInfo(@TypeOf(value))) {
.Int => {
return self.writeInt(value);
},
.Float => {
return self.writeFloat(value);
},
.Pointer => {
return self.write(value);
},
else => {
@compileError("Unable to print type '" ++ @typeName(@TypeOf(value)) ++ "'");
},
}
}
{#code|poc_printValue_fn.zig#}
fn write(self: *Writer, value: []const u8) !void {
_ = self;
_ = value;
}
fn writeInt(self: *Writer, value: anytype) !void {
_ = self;
_ = value;
}
fn writeFloat(self: *Writer, value: anytype) !void {
_ = self;
_ = value;
}
};
{#code_end#}
<p>
And now, what happens if we give too many arguments to {#syntax#}print{#endsyntax#}?
</p>
{#code_begin|test_err|test_print_too_many_args|unused argument in 'here is a string: '{s}' here is a number: {}#}
const print = @import("std").debug.print;
{#code|test_print_too_many_args.zig#}
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,
});
}
{#code_end#}
<p>
Zig gives programmers the tools needed to protect themselves against their own mistakes.
</p>
@@ -7798,17 +4060,8 @@ test "print too many arguments" {
Zig doesn't care whether the format argument is a string literal,
only that it is a compile-time known value that can be coerced to a {#syntax#}[]const u8{#endsyntax#}:
</p>
{#code_begin|exe|print_comptime-known_format#}
const print = @import("std").debug.print;
{#code|print_comptime-known_format.zig#}
const a_number: i32 = 1234;
const a_string = "foobar";
const fmt = "here is a string: '{s}' here is a number: {}\n";
pub fn main() void {
print(fmt, .{a_string, a_number});
}
{#code_end#}
<p>
This works fine.
</p>
@@ -7827,103 +4080,13 @@ pub fn main() void {
can use inline assembly. Here is an example of implementing Hello, World on x86_64 Linux
using inline assembly:
</p>
{#code_begin|exe|inline_assembly#}
{#target_linux_x86_64#}
pub fn main() noreturn {
const msg = "hello world\n";
_ = syscall3(SYS_write, STDOUT_FILENO, @intFromPtr(msg), msg.len);
_ = syscall1(SYS_exit, 0);
unreachable;
}
{#code|inline_assembly.zig#}
pub const SYS_write = 1;
pub const SYS_exit = 60;
pub const STDOUT_FILENO = 1;
pub fn syscall1(number: usize, arg1: usize) usize {
return asm volatile ("syscall"
: [ret] "={rax}" (-> usize),
: [number] "{rax}" (number),
[arg1] "{rdi}" (arg1),
: "rcx", "r11"
);
}
pub fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize {
return asm volatile ("syscall"
: [ret] "={rax}" (-> usize),
: [number] "{rax}" (number),
[arg1] "{rdi}" (arg1),
[arg2] "{rsi}" (arg2),
[arg3] "{rdx}" (arg3),
: "rcx", "r11"
);
}
{#code_end#}
<p>
Dissecting the syntax:
</p>
{#code_begin|syntax|Assembly Syntax Explained#}
pub fn syscall1(number: usize, arg1: usize) usize {
// Inline assembly is an expression which returns a value.
// the `asm` keyword begins the expression.
return asm
// `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"
);
}
{#code_end#}
{#code|Assembly Syntax Explained.zig#}
<p>
For x86 and x86_64 targets, the syntax is AT&T syntax, rather than the more
popular Intel syntax. This is due to technical constraints; assembly parsing is
@@ -7991,27 +4154,8 @@ pub fn syscall1(number: usize, arg1: usize) usize {
verbatim into one long string and assembled together. There are no template substitution rules regarding
<code>%</code> as there are in inline assembly expressions.
</p>
{#code_begin|test|test_global_assembly#}
{#target_linux_x86_64#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_global_assembly.zig#}
comptime {
asm (
\\.global my_func;
\\.type my_func, @function;
\\my_func:
\\ lea (%rdi,%rsi,1),%eax
\\ retq
);
}
extern fn my_func(a: i32, b: i32) i32;
test "global assembly" {
try expect(my_func(12, 34) == 46);
}
{#code_end#}
{#header_close#}
{#header_close#}
@@ -8254,56 +4398,14 @@ comptime {
<p>
Calls a function, in the same way that invoking an expression with parentheses does:
</p>
{#code_begin|test|test_call_builtin#}
const expect = @import("std").testing.expect;
{#code|test_call_builtin.zig#}
test "noinline function call" {
try expect(@call(.auto, add, .{3, 9}) == 12);
}
fn add(a: i32, b: i32) i32 {
return a + b;
}
{#code_end#}
<p>
{#syntax#}@call{#endsyntax#} allows more flexibility than normal function call syntax does. The
{#syntax#}CallModifier{#endsyntax#} enum is reproduced here:
</p>
{#code_begin|syntax|builtin.CallModifier struct#}
pub const CallModifier = enum {
/// Equivalent to function call syntax.
auto,
{#code|builtin.CallModifier struct.zig#}
/// 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 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,
};
{#code_end#}
{#header_close#}
{#header_open|@cDefine#}
@@ -8389,17 +4491,8 @@ pub const CallModifier = enum {
if the current value is not the given expected value. It's the equivalent of this code,
except atomic:
</p>
{#code_begin|syntax|not_atomic_cmpxchgStrong#}
fn cmpxchgStrongButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_value: T) ?T {
const old_value = ptr.*;
if (old_value == expected_value) {
ptr.* = new_value;
return null;
} else {
return old_value;
}
}
{#code_end#}
{#code|not_atomic_cmpxchgStrong.zig#}
<p>
If you are using cmpxchg in a retry loop, {#link|@cmpxchgWeak#} is the better choice, because it can be implemented
more efficiently in machine instructions.
@@ -8473,22 +4566,8 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
This function can be used to do "printf debugging" on
compile-time executing code.
</p>
{#code_begin|test_err|test_compileLog_builtin|found compile log statement#}
const print = @import("std").debug.print;
{#code|test_compileLog_builtin.zig#}
const num1 = blk: {
var val1: i32 = 99;
@compileLog("comptime val1 = ", val1);
val1 = val1 + 1;
break :blk val1;
};
test "main" {
@compileLog("comptime in main");
print("Runtime in main, num1 = {}.\n", .{num1});
}
{#code_end#}
{#header_close#}
{#header_open|@constCast#}
@@ -8695,22 +4774,15 @@ test "main" {
{#syntax#}options.linkage{#endsyntax#} is {#syntax#}Strong{#endsyntax#}, this is equivalent to
the {#syntax#}export{#endsyntax#} keyword used on a function:
</p>
{#code_begin|obj|export_builtin#}
comptime {
@export(internalName, .{ .name = "foo", .linkage = .strong });
}
{#code|export_builtin.zig#}
fn internalName() callconv(.C) void {}
{#code_end#}
<p>This is equivalent to:</p>
{#code_begin|obj|export_builtin_equivalent_code#}
export fn foo() void {}
{#code_end#}
{#code|export_builtin_equivalent_code.zig#}
<p>Note that even when using {#syntax#}export{#endsyntax#}, the {#syntax#}@"foo"{#endsyntax#} syntax for
{#link|identifiers|Identifiers#} can be used to choose any string for the symbol name:</p>
{#code_begin|obj|export_any_symbol_name#}
export fn @"A function name that is a complete sentence."() void {}
{#code_end#}
{#code|export_any_symbol_name.zig#}
<p>
When looking at the resulting object, you can see the symbol is used verbatim:
</p>
@@ -8740,36 +4812,8 @@ export fn @"A function name that is a complete sentence."() void {}
<pre>{#syntax#}@field(lhs: anytype, comptime field_name: []const u8) (field){#endsyntax#}</pre>
<p>Performs field access by a compile-time string. Works on both fields and declarations.
</p>
{#code_begin|test|test_field_builtin#}
const std = @import("std");
{#code|test_field_builtin.zig#}
const Point = struct {
x: u32,
y: u32,
pub var z: u32 = 1;
};
test "field access by string" {
const expect = std.testing.expect;
var p = Point{ .x = 0, .y = 0 };
@field(p, "x") = 4;
@field(p, "y") = @field(p, "x") + 1;
try expect(@field(p, "x") == 4);
try expect(@field(p, "y") == 5);
}
test "decl access by string" {
const expect = std.testing.expect;
try expect(@field(Point, "z") == 1);
@field(Point, "z") = 2;
try expect(@field(Point, "z") == 2);
}
{#code_end#}
{#header_close#}
@@ -8818,30 +4862,8 @@ test "decl access by string" {
Returns whether or not a {#link|container|Containers#} has a declaration
matching {#syntax#}name{#endsyntax#}.
</p>
{#code_begin|test|test_hasDecl_builtin#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_hasDecl_builtin.zig#}
const Foo = struct {
nope: i32,
pub var 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"));
}
{#code_end#}
{#see_also|@hasField#}
{#header_close#}
@@ -8910,14 +4932,8 @@ test "@hasDecl" {
Attempting to convert a number which is out of range of the destination type results in
safety-protected {#link|Undefined Behavior#}.
</p>
{#code_begin|test_err|test_intCast_builtin|cast truncated bits#}
test "integer cast panic" {
var a: u16 = 0xabcd; // runtime-known
_ = &a;
const b: u8 = @intCast(a);
_ = b;
}
{#code_end#}
{#code|test_intCast_builtin.zig#}
<p>
To truncate the significant bits of a number out of range of the destination type, use {#link|@truncate#}.
</p>
@@ -9066,19 +5082,8 @@ test "integer cast panic" {
designers targeting Wasm. So unless you are writing a new allocator from scratch, you should use
something like {#syntax#}@import("std").heap.WasmPageAllocator{#endsyntax#}.
</p>
{#code_begin|test|test_wasmMemoryGrow_builtin#}
const std = @import("std");
const native_arch = @import("builtin").target.cpu.arch;
const expect = std.testing.expect;
{#code|test_wasmMemoryGrow_builtin.zig#}
test "@wasmMemoryGrow" {
if (native_arch != .wasm32) return error.SkipZigTest;
const prev = @wasmMemorySize(0);
try expect(prev == @wasmMemoryGrow(0, 1));
try expect(prev + 1 == @wasmMemorySize(0));
}
{#code_end#}
{#see_also|@wasmMemorySize#}
{#header_close#}
@@ -9257,24 +5262,11 @@ test "@wasmMemoryGrow" {
<p>
Example:
</p>
{#code_begin|test_err|test_without_setEvalBranchQuota_builtin|evaluation exceeded 1000 backwards branches#}
test "foo" {
comptime {
var i = 0;
while (i < 1001) : (i += 1) {}
}
}
{#code_end#}
{#code|test_without_setEvalBranchQuota_builtin.zig#}
<p>Now we use {#syntax#}@setEvalBranchQuota{#endsyntax#}:</p>
{#code_begin|test|test_setEvalBranchQuota_builtin#}
test "foo" {
comptime {
@setEvalBranchQuota(1001);
var i = 0;
while (i < 1001) : (i += 1) {}
}
}
{#code_end#}
{#code|test_setEvalBranchQuota_builtin.zig#}
{#see_also|comptime#}
{#header_close#}
@@ -9312,30 +5304,8 @@ test "foo" {
<p>
Sets whether runtime safety checks are enabled for the scope that contains the function call.
</p>
{#code_begin|test_safety|test_setRuntimeSafety_builtin|integer overflow#}
{#code_release_fast#}
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; // undefined 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;
{#code|test_setRuntimeSafety_builtin.zig#}
{
// 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; // undefined behavior in all build modes.
}
}
}
{#code_end#}
<p>Note: it is <a href="https://github.com/ziglang/zig/issues/978">planned</a> to replace
{#syntax#}@setRuntimeSafety{#endsyntax#} with <code>@optimizeFor</code></p>
@@ -9420,26 +5390,8 @@ test "@setRuntimeSafety" {
{#link|pointer|Pointers#}, or {#syntax#}bool{#endsyntax#}. The mask may be any vector length, and its
length determines the result length.
</p>
{#code_begin|test|test_shuffle_builtin#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_shuffle_builtin.zig#}
test "vector @shuffle" {
const a = @Vector(7, u8){ 'o', 'l', 'h', 'e', 'r', 'z', 'w' };
const b = @Vector(4, u8){ 'w', 'd', '!', 'x' };
// To shuffle within a single vector, pass undefined as the second argument.
// Notice that we can re-order, duplicate, or omit elements of the input vector
const mask1 = @Vector(5, i32){ 2, 3, 1, 1, 0 };
const res1: @Vector(5, u8) = @shuffle(u8, a, undefined, mask1);
try expect(std.mem.eql(u8, &@as([5]u8, res1), "hello"));
// Combining two vectors
const mask2 = @Vector(6, i32){ -1, 0, 4, 1, -2, -3 };
const res2: @Vector(6, u8) = @shuffle(u8, a, b, mask2);
try expect(std.mem.eql(u8, &@as([6]u8, res2), "world!"));
}
{#code_end#}
{#see_also|Vectors#}
{#header_close#}
@@ -9468,16 +5420,8 @@ test "vector @shuffle" {
Produces a vector where each element is the value {#syntax#}scalar{#endsyntax#}.
The return type and thus the length of the vector is inferred.
</p>
{#code_begin|test|test_splat_builtin#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_splat_builtin.zig#}
test "vector @splat" {
const scalar: u32 = 5;
const result: @Vector(4, u32) = @splat(scalar);
try expect(std.mem.eql(u32, &@as([4]u32, result), &[_]u32{ 5, 5, 5, 5 }));
}
{#code_end#}
<p>
{#syntax#}scalar{#endsyntax#} must be an {#link|integer|Integers#}, {#link|bool|Primitive Types#},
{#link|float|Floats#}, or {#link|pointer|Pointers#}.
@@ -9510,21 +5454,8 @@ test "vector @splat" {
types the operation associativity is preserved, unless the float mode is
set to {#syntax#}Optimized{#endsyntax#}.
</p>
{#code_begin|test|test_reduce_builtin#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_reduce_builtin.zig#}
test "vector @reduce" {
const V = @Vector(4, i32);
const value = V{ 1, -1, 1, -1 };
const result = value > @as(V, @splat(0));
// result is { true, false, true, false };
try comptime expect(@TypeOf(result) == @Vector(4, bool));
const is_all_true = @reduce(.And, result);
try comptime expect(@TypeOf(is_all_true) == bool);
try expect(is_all_true == false);
}
{#code_end#}
{#see_also|Vectors|@setFloatMode#}
{#header_close#}
@@ -9533,23 +5464,8 @@ test "vector @reduce" {
<p>
Returns a {#syntax#}SourceLocation{#endsyntax#} struct representing the function's name and location in the source code. This must be called in a function.
</p>
{#code_begin|test|test_src_builtin#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_src_builtin.zig#}
test "@src" {
try doTheTest();
}
fn doTheTest() !void {
const src = @src();
try expect(src.line == 9);
try expect(src.column == 17);
try expect(std.mem.endsWith(u8, src.fn_name, "doTheTest"));
try expect(std.mem.endsWith(u8, src.file, "test_src_builtin.zig"));
}
{#code_end#}
{#header_close#}
{#header_open|@sqrt#}
<pre>{#syntax#}@sqrt(value: anytype) @TypeOf(value){#endsyntax#}</pre>
@@ -9718,28 +5634,8 @@ fn doTheTest() !void {
Returns the innermost struct, enum, or union that this function call is inside.
This can be useful for an anonymous struct that needs to refer to itself:
</p>
{#code_begin|test|test_this_builtin#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_this_builtin.zig#}
test "@This()" {
var items = [_]i32{ 1, 2, 3, 4 };
const list = List(i32){ .items = items[0..] };
try expect(list.length() == 4);
}
fn List(comptime T: type) type {
return struct {
const Self = @This();
items: []T,
fn length(self: Self) usize {
return self.items.len;
}
};
}
{#code_end#}
<p>
When {#syntax#}@This(){#endsyntax#} is used at file scope, it returns a reference to the
struct that corresponds to the current file.
@@ -9772,16 +5668,8 @@ fn List(comptime T: type) type {
<p>
Calling {#syntax#}@truncate{#endsyntax#} on a number out of range of the destination type is well defined and working code:
</p>
{#code_begin|test|test_truncate_builtin#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_truncate_builtin.zig#}
test "integer truncation" {
const a: u16 = 0xabcd;
const b: u8 = @truncate(a);
try expect(b == 0xcd);
}
{#code_end#}
<p>
Use {#link|@intCast#} to convert numbers guaranteed to fit the destination type.
</p>
@@ -9858,22 +5746,8 @@ test "integer truncation" {
<p>
The expressions are evaluated, however they are guaranteed to have no <em>runtime</em> side-effects:
</p>
{#code_begin|test|test_TypeOf_builtin#}
const std = @import("std");
const expect = std.testing.expect;
{#code|test_TypeOf_builtin.zig#}
test "no runtime side effects" {
var data: i32 = 0;
const T = @TypeOf(foo(i32, &data));
try comptime expect(T == i32);
try expect(data == 0);
}
fn foo(comptime T: type, ptr: *T) T {
ptr.* += 1;
return ptr.*;
}
{#code_end#}
{#header_close#}
{#header_open|@unionInit#}
@@ -9936,19 +5810,8 @@ fn foo(comptime T: type, ptr: *T) T {
<p>
To add standard build options to a <code class="file">build.zig</code> file:
</p>
{#code_begin|syntax|build#}
const std = @import("std");
{#code|build.zig#}
pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "example",
.root_source_file = .{ .path = "example.zig" },
.optimize = optimize,
});
b.default_step.dependOn(&exe.step);
}
{#code_end#}
<p>
This causes these options to be available:
</p>
@@ -10026,95 +5889,42 @@ pub fn build(b: *std.Build) void {
<p>
When a safety check fails, Zig crashes with a stack trace, like this:
</p>
{#code_begin|test_err|test_undefined_behavior|reached unreachable code#}
test "safety check" {
unreachable;
}
{#code_end#}
{#code|test_undefined_behavior.zig#}
{#header_open|Reaching Unreachable Code#}
<p>At compile-time:</p>
{#code_begin|test_err|test_comptime_reaching_unreachable|reached unreachable code#}
comptime {
assert(false);
}
fn assert(ok: bool) void {
if (!ok) unreachable; // assertion failure
}
{#code_end#}
<p>At runtime:</p>
{#code_begin|exe_err|runtime_reaching_unreachable#}
const std = @import("std");
{#code|test_comptime_reaching_unreachable.zig#}
<p>At runtime:</p>
{#code|runtime_reaching_unreachable.zig#}
pub fn main() void {
std.debug.assert(false);
}
{#code_end#}
{#header_close#}
{#header_open|Index out of Bounds#}
<p>At compile-time:</p>
{#code_begin|test_err|test_comptime_index_out_of_bounds|index 5 outside array of length 5#}
comptime {
const array: [5]u8 = "hello".*;
const garbage = array[5];
_ = garbage;
}
{#code_end#}
<p>At runtime:</p>
{#code_begin|exe_err|runtime_index_out_of_bounds#}
pub fn main() void {
const x = foo("hello");
_ = x;
}
{#code|test_comptime_index_out_of_bounds.zig#}
<p>At runtime:</p>
{#code|runtime_index_out_of_bounds.zig#}
fn foo(x: []const u8) u8 {
return x[5];
}
{#code_end#}
{#header_close#}
{#header_open|Cast Negative Number to Unsigned Integer#}
<p>At compile-time:</p>
{#code_begin|test_err|test_comptime_invalid_cast|type 'u32' cannot represent integer value '-1'#}
comptime {
const value: i32 = -1;
const unsigned: u32 = @intCast(value);
_ = unsigned;
}
{#code_end#}
<p>At runtime:</p>
{#code_begin|exe_err|runtime_invalid_cast#}
const std = @import("std");
{#code|test_comptime_invalid_cast.zig#}
<p>At runtime:</p>
{#code|runtime_invalid_cast.zig#}
pub fn main() void {
var value: i32 = -1; // runtime-known
_ = &value;
const unsigned: u32 = @intCast(value);
std.debug.print("value: {}\n", .{unsigned});
}
{#code_end#}
<p>
To obtain the maximum value of an unsigned integer, use {#syntax#}std.math.maxInt{#endsyntax#}.
</p>
{#header_close#}
{#header_open|Cast Truncates Data#}
<p>At compile-time:</p>
{#code_begin|test_err|test_comptime_invalid_cast_truncate|type 'u8' cannot represent integer value '300'#}
comptime {
const spartan_count: u16 = 300;
const byte: u8 = @intCast(spartan_count);
_ = byte;
}
{#code_end#}
<p>At runtime:</p>
{#code_begin|exe_err|runtime_invalid_cast_truncate#}
const std = @import("std");
{#code|test_comptime_invalid_cast_truncate.zig#}
<p>At runtime:</p>
{#code|runtime_invalid_cast_truncate.zig#}
pub fn main() void {
var spartan_count: u16 = 300; // runtime-known
_ = &spartan_count;
const byte: u8 = @intCast(spartan_count);
std.debug.print("value: {}\n", .{byte});
}
{#code_end#}
<p>
To truncate bits, use {#link|@truncate#}.
</p>
@@ -10133,22 +5943,11 @@ pub fn main() void {
<li>{#link|@divExact#} (division)</li>
</ul>
<p>Example with addition at compile-time:</p>
{#code_begin|test_err|test_comptime_overflow|overflow of integer type 'u8' with value '256'#}
comptime {
var byte: u8 = 255;
byte += 1;
}
{#code_end#}
<p>At runtime:</p>
{#code_begin|exe_err|runtime_overflow#}
const std = @import("std");
{#code|test_comptime_overflow.zig#}
<p>At runtime:</p>
{#code|runtime_overflow.zig#}
pub fn main() void {
var byte: u8 = 255;
byte += 1;
std.debug.print("value: {}\n", .{byte});
}
{#code_end#}
{#header_close#}
{#header_open|Standard Library Math Functions#}
<p>These functions provided by the standard library return possible errors.</p>
@@ -10162,20 +5961,8 @@ pub fn main() void {
<li>{#syntax#}@import("std").math.shl{#endsyntax#}</li>
</ul>
<p>Example of catching an overflow for addition:</p>
{#code_begin|exe_err|math_add#}
const math = @import("std").math;
const print = @import("std").debug.print;
pub fn main() !void {
var byte: u8 = 255;
{#code|math_add.zig#}
byte = if (math.add(u8, byte, 1)) |result| result else |err| {
print("unable to add one: {s}\n", .{@errorName(err)});
return err;
};
print("result: {}\n", .{byte});
}
{#code_end#}
{#header_close#}
{#header_open|Builtin Overflow Functions#}
<p>
@@ -10191,19 +5978,8 @@ pub fn main() !void {
<p>
Example of {#link|@addWithOverflow#}:
</p>
{#code_begin|exe|addWithOverflow_builtin#}
const print = @import("std").debug.print;
pub fn main() void {
const byte: u8 = 255;
{#code|addWithOverflow_builtin.zig#}
const ov = @addWithOverflow(byte, 10);
if (ov[1] != 0) {
print("overflowed result: {}\n", .{ov[0]});
} else {
print("result: {}\n", .{ov[0]});
}
}
{#code_end#}
{#header_close#}
{#header_open|Wrapping Operations#}
<p>
@@ -10215,409 +5991,131 @@ pub fn main() void {
<li>{#syntax#}-%{#endsyntax#} (wraparound negation)</li>
<li>{#syntax#}*%{#endsyntax#} (wraparound multiplication)</li>
</ul>
{#code_begin|test|test_wraparound_semantics#}
const std = @import("std");
const expect = std.testing.expect;
const minInt = std.math.minInt;
const maxInt = std.math.maxInt;
{#code|test_wraparound_semantics.zig#}
test "wraparound addition and subtraction" {
const x: i32 = maxInt(i32);
const min_val = x +% 1;
try expect(min_val == minInt(i32));
const max_val = min_val -% 1;
try expect(max_val == maxInt(i32));
}
{#code_end#}
{#header_close#}
{#header_close#}
{#header_open|Exact Left Shift Overflow#}
<p>At compile-time:</p>
{#code_begin|test_err|test_comptime_shlExact_overwlow|operation caused overflow#}
comptime {
const x = @shlExact(@as(u8, 0b01010101), 2);
_ = x;
}
{#code_end#}
<p>At runtime:</p>
{#code_begin|exe_err|runtime_shlExact_overflow#}
const std = @import("std");
{#code|test_comptime_shlExact_overwlow.zig#}
<p>At runtime:</p>
{#code|runtime_shlExact_overflow.zig#}
pub fn main() void {
var x: u8 = 0b01010101; // runtime-known
_ = &x;
const y = @shlExact(x, 2);
std.debug.print("value: {}\n", .{y});
}
{#code_end#}
{#header_close#}
{#header_open|Exact Right Shift Overflow#}
<p>At compile-time:</p>
{#code_begin|test_err|test_comptime_shrExact_overflow|exact shift shifted out 1 bits#}
comptime {
const x = @shrExact(@as(u8, 0b10101010), 2);
_ = x;
}
{#code_end#}
<p>At runtime:</p>
{#code_begin|exe_err|runtime_shrExact_overflow#}
const std = @import("std");
{#code|test_comptime_shrExact_overflow.zig#}
<p>At runtime:</p>
{#code|runtime_shrExact_overflow.zig#}
pub fn main() void {
var x: u8 = 0b10101010; // runtime-known
_ = &x;
const y = @shrExact(x, 2);
std.debug.print("value: {}\n", .{y});
}
{#code_end#}
{#header_close#}
{#header_open|Division by Zero#}
<p>At compile-time:</p>
{#code_begin|test_err|test_comptime_division_by_zero|division by zero#}
comptime {
const a: i32 = 1;
const b: i32 = 0;
const c = a / b;
_ = c;
}
{#code_end#}
<p>At runtime:</p>
{#code_begin|exe_err|runtime_division_by_zero#}
const std = @import("std");
{#code|test_comptime_division_by_zero.zig#}
<p>At runtime:</p>
{#code|runtime_division_by_zero.zig#}
pub fn main() void {
var a: u32 = 1;
var b: u32 = 0;
_ = .{ &a, &b };
const c = a / b;
std.debug.print("value: {}\n", .{c});
}
{#code_end#}
{#header_close#}
{#header_open|Remainder Division by Zero#}
<p>At compile-time:</p>
{#code_begin|test_err|test_comptime_remainder_division_by_zero|division by zero#}
comptime {
const a: i32 = 10;
const b: i32 = 0;
const c = a % b;
_ = c;
}
{#code_end#}
<p>At runtime:</p>
{#code_begin|exe_err|runtime_remainder_division_by_zero#}
const std = @import("std");
{#code|test_comptime_remainder_division_by_zero.zig#}
<p>At runtime:</p>
{#code|runtime_remainder_division_by_zero.zig#}
pub fn main() void {
var a: u32 = 10;
var b: u32 = 0;
_ = .{ &a, &b };
const c = a % b;
std.debug.print("value: {}\n", .{c});
}
{#code_end#}
{#header_close#}
{#header_open|Exact Division Remainder#}
<p>At compile-time:</p>
{#code_begin|test_err|test_comptime_divExact_remainder|exact division produced remainder#}
comptime {
const a: u32 = 10;
const b: u32 = 3;
const c = @divExact(a, b);
_ = c;
}
{#code_end#}
<p>At runtime:</p>
{#code_begin|exe_err|runtime_divExact_remainder#}
const std = @import("std");
{#code|test_comptime_divExact_remainder.zig#}
<p>At runtime:</p>
{#code|runtime_divExact_remainder.zig#}
pub fn main() void {
var a: u32 = 10;
var b: u32 = 3;
_ = .{ &a, &b };
const c = @divExact(a, b);
std.debug.print("value: {}\n", .{c});
}
{#code_end#}
{#header_close#}
{#header_open|Attempt to Unwrap Null#}
<p>At compile-time:</p>
{#code_begin|test_err|test_comptime_unwrap_null|unable to unwrap null#}
comptime {
const optional_number: ?i32 = null;
const number = optional_number.?;
_ = number;
}
{#code_end#}
<p>At runtime:</p>
{#code_begin|exe_err|runtime_unwrap_null#}
const std = @import("std");
{#code|test_comptime_unwrap_null.zig#}
<p>At runtime:</p>
{#code|runtime_unwrap_null.zig#}
pub fn main() void {
var optional_number: ?i32 = null;
_ = &optional_number;
const number = optional_number.?;
std.debug.print("value: {}\n", .{number});
}
{#code_end#}
<p>One way to avoid this crash is to test for null instead of assuming non-null, with
the {#syntax#}if{#endsyntax#} expression:</p>
{#code_begin|exe|testing_null_with_if#}
const print = @import("std").debug.print;
pub fn main() void {
const optional_number: ?i32 = null;
{#code|testing_null_with_if.zig#}
if (optional_number) |number| {
print("got number: {}\n", .{number});
} else {
print("it's null\n", .{});
}
}
{#code_end#}
{#see_also|Optionals#}
{#header_close#}
{#header_open|Attempt to Unwrap Error#}
<p>At compile-time:</p>
{#code_begin|test_err|test_comptime_unwrap_error|caught unexpected error 'UnableToReturnNumber'#}
comptime {
const number = getNumberOrFail() catch unreachable;
_ = number;
}
{#code|test_comptime_unwrap_error.zig#}
fn getNumberOrFail() !i32 {
return error.UnableToReturnNumber;
}
{#code_end#}
<p>At runtime:</p>
{#code_begin|exe_err|runtime_unwrap_error#}
const std = @import("std");
{#code|runtime_unwrap_error.zig#}
pub fn main() void {
const number = getNumberOrFail() catch unreachable;
std.debug.print("value: {}\n", .{number});
}
fn getNumberOrFail() !i32 {
return error.UnableToReturnNumber;
}
{#code_end#}
<p>One way to avoid this crash is to test for an error instead of assuming a successful result, with
the {#syntax#}if{#endsyntax#} expression:</p>
{#code_begin|exe|testing_error_with_if#}
const print = @import("std").debug.print;
{#code|testing_error_with_if.zig#}
pub fn main() void {
const result = getNumberOrFail();
if (result) |number| {
print("got number: {}\n", .{number});
} else |err| {
print("got error: {s}\n", .{@errorName(err)});
}
}
fn getNumberOrFail() !i32 {
return error.UnableToReturnNumber;
}
{#code_end#}
{#see_also|Errors#}
{#header_close#}
{#header_open|Invalid Error Code#}
<p>At compile-time:</p>
{#code_begin|test_err|test_comptime_invalid_error_code|integer value '11' represents no error#}
comptime {
const err = error.AnError;
const number = @intFromError(err) + 10;
const invalid_err = @errorFromInt(number);
_ = invalid_err;
}
{#code_end#}
<p>At runtime:</p>
{#code_begin|exe_err|runtime_invalid_error_code#}
const std = @import("std");
{#code|test_comptime_invalid_error_code.zig#}
<p>At runtime:</p>
{#code|runtime_invalid_error_code.zig#}
pub fn main() void {
const err = error.AnError;
var number = @intFromError(err) + 500;
_ = &number;
const invalid_err = @errorFromInt(number);
std.debug.print("value: {}\n", .{invalid_err});
}
{#code_end#}
{#header_close#}
{#header_open|Invalid Enum Cast#}
<p>At compile-time:</p>
{#code_begin|test_err|test_comptime_invalid_enum_cast|enum 'test_comptime_invalid_enum_cast.Foo' has no tag with value '3'#}
const Foo = enum {
a,
b,
c,
};
comptime {
const a: u2 = 3;
const b: Foo = @enumFromInt(a);
_ = b;
}
{#code_end#}
{#code|test_comptime_invalid_enum_cast.zig#}
<p>At runtime:</p>
{#code_begin|exe_err|runtime_invalid_enum_cast#}
const std = @import("std");
{#code|runtime_invalid_enum_cast.zig#}
const Foo = enum {
a,
b,
c,
};
pub fn main() void {
var a: u2 = 3;
_ = &a;
const b: Foo = @enumFromInt(a);
std.debug.print("value: {s}\n", .{@tagName(b)});
}
{#code_end#}
{#header_close#}
{#header_open|Invalid Error Set Cast#}
<p>At compile-time:</p>
{#code_begin|test_err|test_comptime_invalid_error_set_cast|'error.B' not a member of error set 'error{A,C}'#}
const Set1 = error{
A,
B,
};
const Set2 = error{
A,
C,
};
comptime {
_ = @as(Set2, @errorCast(Set1.B));
}
{#code_end#}
<p>At runtime:</p>
{#code_begin|exe_err|runtime_invalid_error_set_cast#}
const std = @import("std");
{#code|test_comptime_invalid_error_set_cast.zig#}
<p>At runtime:</p>
{#code|runtime_invalid_error_set_cast.zig#}
const Set1 = error{
A,
B,
};
const Set2 = error{
A,
C,
};
pub fn main() void {
foo(Set1.B);
}
fn foo(set1: Set1) void {
const x: Set2 = @errorCast(set1);
std.debug.print("value: {}\n", .{x});
}
{#code_end#}
{#header_close#}
{#header_open|Incorrect Pointer Alignment#}
<p>At compile-time:</p>
{#code_begin|test_err|test_comptime_incorrect_pointer_alignment|pointer address 0x1 is not aligned to 4 bytes#}
comptime {
const ptr: *align(1) i32 = @ptrFromInt(0x1);
const aligned: *align(4) i32 = @alignCast(ptr);
_ = aligned;
}
{#code_end#}
{#code|test_comptime_incorrect_pointer_alignment.zig#}
<p>At runtime:</p>
{#code_begin|exe_err|runtime_incorrect_pointer_alignment#}
const mem = @import("std").mem;
pub fn main() !void {
var array align(4) = [_]u32{ 0x11111111, 0x11111111 };
const bytes = mem.sliceAsBytes(array[0..]);
if (foo(bytes) != 0x11111111) return error.Wrong;
}
fn foo(bytes: []u8) u32 {
const slice4 = bytes[1..5];
const int_slice = mem.bytesAsSlice(u32, @as([]align(4) u8, @alignCast(slice4)));
return int_slice[0];
}
{#code_end#}
{#code|runtime_incorrect_pointer_alignment.zig#}
{#header_close#}
{#header_open|Wrong Union Field Access#}
<p>At compile-time:</p>
{#code_begin|test_err|test_comptime_wrong_union_field_access|access of union field 'float' while field 'int' is active#}
comptime {
var f = Foo{ .int = 42 };
f.float = 12.34;
}
{#code|test_comptime_wrong_union_field_access.zig#}
const Foo = union {
float: f32,
int: u32,
};
{#code_end#}
<p>At runtime:</p>
{#code_begin|exe_err|runtime_wrong_union_field_access#}
const std = @import("std");
{#code|runtime_wrong_union_field_access.zig#}
const Foo = union {
float: f32,
int: u32,
};
pub fn main() void {
var f = Foo{ .int = 42 };
bar(&f);
}
fn bar(f: *Foo) void {
f.float = 12.34;
std.debug.print("value: {}\n", .{f.float});
}
{#code_end#}
<p>
This safety is not available for {#syntax#}extern{#endsyntax#} or {#syntax#}packed{#endsyntax#} unions.
</p>
<p>
To change the active field of a union, assign the entire union, like this:
</p>
{#code_begin|exe|change_active_union_field#}
const std = @import("std");
{#code|change_active_union_field.zig#}
const Foo = union {
float: f32,
int: u32,
};
pub fn main() void {
var f = Foo{ .int = 42 };
bar(&f);
}
fn bar(f: *Foo) void {
f.* = Foo{ .float = 12.34 };
std.debug.print("value: {}\n", .{f.float});
}
{#code_end#}
<p>
To change the active field of a union when a meaningful value for the field is not known,
use {#link|undefined#}, like this:
</p>
{#code_begin|exe|undefined_active_union_field#}
const std = @import("std");
{#code|undefined_active_union_field.zig#}
const Foo = union {
float: f32,
int: u32,
};
pub fn main() void {
var f = Foo{ .int = 42 };
f = Foo{ .float = undefined };
bar(&f);
std.debug.print("value: {}\n", .{f.float});
}
fn bar(f: *Foo) void {
f.float = 12.34;
}
{#code_end#}
{#see_also|union|extern union#}
{#header_close#}
@@ -10627,22 +6125,11 @@ fn bar(f: *Foo) void {
integer type's range.
</p>
<p>At compile-time:</p>
{#code_begin|test_err|test_comptime_out_of_bounds_float_to_integer_cast|float value '4294967296' cannot be stored in integer type 'i32'#}
comptime {
const float: f32 = 4294967296;
const int: i32 = @intFromFloat(float);
_ = int;
}
{#code_end#}
{#code|test_comptime_out_of_bounds_float_to_integer_cast.zig#}
<p>At runtime:</p>
{#code_begin|exe_err|runtime_out_of_bounds_float_to_integer_cast#}
pub fn main() void {
var float: f32 = 4294967296; // runtime-known
_ = &float;
const int: i32 = @intFromFloat(float);
_ = int;
}
{#code_end#}
{#code|runtime_out_of_bounds_float_to_integer_cast.zig#}
{#header_close#}
{#header_open|Pointer Cast Invalid Null#}
@@ -10652,22 +6139,11 @@ pub fn main() void {
allow address zero, but normal {#link|Pointers#} do not.
</p>
<p>At compile-time:</p>
{#code_begin|test_err|test_comptime_invalid_null_pointer_cast|null pointer casted to type#}
comptime {
const opt_ptr: ?*i32 = null;
const ptr: *i32 = @ptrCast(opt_ptr);
_ = ptr;
}
{#code_end#}
{#code|test_comptime_invalid_null_pointer_cast.zig#}
<p>At runtime:</p>
{#code_begin|exe_err|runtime_invalid_null_pointer_cast#}
pub fn main() void {
var opt_ptr: ?*i32 = null;
_ = &opt_ptr;
const ptr: *i32 = @ptrCast(opt_ptr);
_ = ptr;
}
{#code_end#}
{#code|runtime_invalid_null_pointer_cast.zig#}
{#header_close#}
{#header_close#}
@@ -10689,26 +6165,8 @@ pub fn main() void {
{#syntax#}std.ArrayList{#endsyntax#} accept an {#syntax#}Allocator{#endsyntax#} parameter in
their initialization functions:
</p>
{#code_begin|test|test_allocator#}
const std = @import("std");
const Allocator = std.mem.Allocator;
const expect = std.testing.expect;
{#code|test_allocator.zig#}
test "using an allocator" {
var buffer: [100]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buffer);
const allocator = fba.allocator();
const result = try concat(allocator, "foo", "bar");
try expect(std.mem.eql(u8, "foobar", result));
}
fn concat(allocator: Allocator, a: []const u8, b: []const u8) ![]u8 {
const result = try allocator.alloc(u8, a.len + b.len);
@memcpy(result[0..a.len], a);
@memcpy(result[a.len..], b);
return result;
}
{#code_end#}
<p>
In the above example, 100 bytes of stack memory are used to initialize a
{#syntax#}FixedBufferAllocator{#endsyntax#}, which is then passed to a function.
@@ -10743,19 +6201,8 @@ fn concat(allocator: Allocator, a: []const u8, b: []const u8) ![]u8 {
cyclical pattern (such as a video game main loop, or a web server request handler),
such that it would make sense to free everything at once at the end?
In this case, it is recommended to follow this pattern:
{#code_begin|exe|cli_allocation#}
const std = @import("std");
{#code|cli_allocation.zig#}
pub fn main() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = arena.allocator();
const ptr = try allocator.create(i32);
std.debug.print("ptr={*}\n", .{ptr});
}
{#code_end#}
When using this kind of allocator, there is no need to free anything manually. Everything
gets freed at once with the call to {#syntax#}arena.deinit(){#endsyntax#}.
</li>
@@ -10793,25 +6240,11 @@ pub fn main() !void {
<p>String literals such as {#syntax#}"hello"{#endsyntax#} are in the global constant data section.
This is why it is an error to pass a string literal to a mutable slice, like this:
</p>
{#code_begin|test_err|test_string_literal_to_slice|expected type '[]u8', found '*const [5:0]u8'#}
fn foo(s: []u8) void {
_ = s;
}
{#code|test_string_literal_to_slice.zig#}
test "string literal to mutable slice" {
foo("hello");
}
{#code_end#}
<p>However if you make the slice constant, then it works:</p>
{#code_begin|test|test_string_literal_to_const_slice#}
fn foo(s: []const u8) void {
_ = s;
}
{#code|test_string_literal_to_const_slice.zig#}
test "string literal to constant slice" {
foo("hello");
}
{#code_end#}
<p>
Just like string literals, {#syntax#}const{#endsyntax#} declarations, when the value is known at {#link|comptime#},
are stored in the global constant data section. Also {#link|Compile Time Variables#} are stored
@@ -10939,10 +6372,8 @@ test "string literal to constant slice" {
which the compiler makes available to every Zig source file. It contains
compile-time constants such as the current target, endianness, and release mode.
</p>
{#code_begin|syntax|compile_variables#}
const builtin = @import("builtin");
const separator = if (builtin.os.tag == .windows) '\\' else '/';
{#code_end#}
{#code|compile_variables.zig#}
<p>
Example of what is imported with {#syntax#}@import("builtin"){#endsyntax#}:
</p>
@@ -11029,17 +6460,8 @@ const separator = if (builtin.os.tag == .windows) '\\' else '/';
The {#syntax#}@cImport{#endsyntax#} builtin function can be used
to directly import symbols from <code class="file">.h</code> files:
</p>
{#code_begin|exe|cImport_builtin#}
{#link_libc#}
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");
}
{#code_end#}
{#code|cImport_builtin.zig#}
<p>
The {#syntax#}@cImport{#endsyntax#} function takes an expression as a parameter.
This expression is evaluated at compile-time and is used to control
@@ -11147,17 +6569,8 @@ pub extern fn do_something(foo: enum_FOO) c_int;{#end_shell_samp#}
To see where the cached files are stored when compiling code that uses {#syntax#}@cImport{#endsyntax#},
use the <kbd>--verbose-cimport</kbd> flag:
</p>
{#code_begin|exe|verbose_cimport_flag#}
{#link_libc#}
{#code_verbose_cimport#}
const c = @cImport({
@cDefine("_NO_CRT_STDIO_INLINE", "1");
@cInclude("stdio.h");
});
pub fn main() void {
_ = c;
}
{#code_end#}
{#code|verbose_cimport_flag.zig#}
<p>
<code class="file">cimport.h</code> contains the file to translate (constructed from calls to
{#syntax#}@cInclude{#endsyntax#}, {#syntax#}@cDefine{#endsyntax#}, and {#syntax#}@cUndef{#endsyntax#}),
@@ -11211,16 +6624,8 @@ int foo(void) {
}
{#end_syntax_block#}
{#shell_samp#}$ zig translate-c macro.c > macro.zig{#end_shell_samp#}
{#code_begin|syntax|macro#}
pub export fn foo() c_int {
var a: c_int = 1;
_ = &a;
var b: c_int = 2;
_ = &b;
return a + b;
}
pub const MAKELOCAL = @compileError("unable to translate C expr: unexpected token .Equal"); // macro.c:1:9
{#code_end#}
{#code|macro.zig#}
<p>Note that {#syntax#}foo{#endsyntax#} was translated correctly despite using a non-translatable
macro. {#syntax#}MAKELOCAL{#endsyntax#} was demoted to {#syntax#}@compileError{#endsyntax#} since
it cannot be expressed as a Zig function; this simply means that you cannot directly use
@@ -11267,53 +6672,13 @@ pub const MAKELOCAL = @compileError("unable to translate C expr: unexpected toke
{#header_open|C Variadic Functions#}
<p>Zig supports extern variadic functions.</p>
{#code_begin|test|test_variadic_function#}
{#link_libc#}
{#code_verbose_cimport#}
const std = @import("std");
const testing = std.testing;
{#code|test_variadic_function.zig#}
pub extern "c" fn printf(format: [*:0]const u8, ...) c_int;
test "variadic function" {
try testing.expect(printf("Hello, world!\n") == 14);
try testing.expect(@typeInfo(@TypeOf(printf)).Fn.is_var_args);
}
{#code_end#}
<p>
Variadic functions can be implemented using {#link|@cVaStart#}, {#link|@cVaEnd#}, {#link|@cVaArg#} and {#link|@cVaCopy#}.
</p>
{#code_begin|test|test_defining_variadic_function#}
const std = @import("std");
const testing = std.testing;
const builtin = @import("builtin");
{#code|test_defining_variadic_function.zig#}
fn add(count: c_int, ...) callconv(.C) c_int {
var ap = @cVaStart();
defer @cVaEnd(&ap);
var i: usize = 0;
var sum: c_int = 0;
while (i < count) : (i += 1) {
sum += @cVaArg(&ap, c_int);
}
return sum;
}
test "defining a variadic function" {
if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos) {
// https://github.com/ziglang/zig/issues/14096
return error.SkipZigTest;
}
if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) {
// https://github.com/ziglang/zig/issues/16961
return error.SkipZigTest;
}
try std.testing.expectEqual(@as(c_int, 0), add(0));
try std.testing.expectEqual(@as(c_int, 1), add(1, @as(c_int, 1)));
try std.testing.expectEqual(@as(c_int, 3), add(2, @as(c_int, 1), @as(c_int, 2)));
}
{#code_end#}
{#header_close#}
{#header_open|Exporting a C Library#}
<p>
@@ -11321,11 +6686,8 @@ test "defining a variadic function" {
to call into. The {#syntax#}export{#endsyntax#} keyword in front of functions, variables, and types causes them to
be part of the library API:
</p>
{#code_begin|syntax|mathtest#}
export fn add(a: i32, b: i32) i32 {
return a + b;
}
{#code_end#}
{#code|mathtest.zig#}
<p>To make a static library:</p>
{#shell_samp#}$ zig build-lib mathtest.zig{#end_shell_samp#}
<p>To make a shared library:</p>
@@ -11342,30 +6704,8 @@ int main(int argc, char **argv) {
return 0;
}
{#end_syntax_block#}
{#code_begin|syntax|build_c#}
const std = @import("std");
{#code|build_c.zig#}
pub fn build(b: *std.Build) void {
const lib = b.addSharedLibrary(.{
.name = "mathtest",
.root_source_file = .{ .path = "mathtest.zig" },
.version = .{ .major = 1, .minor = 0, .patch = 0 },
});
const exe = b.addExecutable(.{
.name = "test",
});
exe.addCSourceFile(.{ .file = .{ .path = "test.c" }, .flags = &.{"-std=c99"} });
exe.linkLibrary(lib);
exe.linkSystemLibrary("c");
b.default_step.dependOn(&exe.step);
const run_cmd = exe.run();
const test_step = b.step("test", "Test the program");
test_step.dependOn(&run_cmd.step);
}
{#code_end#}
{#shell_samp#}$ zig build test
1379{#end_shell_samp#}
{#see_also|export#}
@@ -11374,23 +6714,8 @@ pub fn build(b: *std.Build) void {
<p>
You can mix Zig object files with any other object files that respect the C ABI. Example:
</p>
{#code_begin|syntax|base64#}
const base64 = @import("std").base64;
{#code|base64.zig#}
export fn decode_base_64(
dest_ptr: [*]u8,
dest_len: usize,
source_ptr: [*]const u8,
source_len: usize,
) usize {
const src = source_ptr[0..source_len];
const dest = dest_ptr[0..dest_len];
const base64_decoder = base64.standard.Decoder;
const decoded_size = base64_decoder.calcSizeForSlice(src) catch unreachable;
base64_decoder.decode(dest[0..decoded_size], src) catch unreachable;
return decoded_size;
}
{#code_end#}
{#syntax_block|c|test.c#}
// This header is generated by zig from base64.zig
#include "base64.h"
@@ -11409,24 +6734,8 @@ int main(int argc, char **argv) {
return 0;
}
{#end_syntax_block#}
{#code_begin|syntax|build_object#}
const std = @import("std");
{#code|build_object.zig#}
pub fn build(b: *std.Build) void {
const obj = b.addObject(.{
.name = "base64",
.root_source_file = .{ .path = "base64.zig" },
});
const exe = b.addExecutable(.{
.name = "test",
});
exe.addCSourceFile(.{ .file = .{ .path = "test.c" }, .flags = &.{"-std=c99",} });
exe.addObject(obj);
exe.linkSystemLibrary("c");
b.installArtifact(exe);
}
{#code_end#}
{#shell_samp#}$ zig build
$ ./zig-out/bin/test
all your base are belong to us{#end_shell_samp#}
@@ -11438,16 +6747,8 @@ all your base are belong to us{#end_shell_samp#}
{#header_open|Freestanding#}
<p>For host environments like the web browser and nodejs, build as an executable using the freestanding
OS target. Here's an example of running Zig code compiled to WebAssembly with nodejs.</p>
{#code_begin|exe|math#}
{#target_wasm#}
{#additonal_option|-fno-entry#}
{#additonal_option|--export=add#}
extern fn print(i32) void;
{#code|math.zig#}
export fn add(a: i32, b: i32) void {
print(a + b);
}
{#code_end#}
{#syntax_block|javascript|test.js#}
const fs = require('fs');
const source = fs.readFileSync("./math.wasm");
@@ -11467,47 +6768,16 @@ The result is 3{#end_shell_samp#}
{#header_open|WASI#}
<p>Zig's support for WebAssembly System Interface (WASI) is under active development.
Example of using the standard library and reading command line arguments:</p>
{#code_begin|exe|wasi_args#}
{#target_wasi#}
const std = @import("std");
{#code|wasi_args.zig#}
pub fn main() !void {
var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
const gpa = general_purpose_allocator.allocator();
const args = try std.process.argsAlloc(gpa);
defer std.process.argsFree(gpa, args);
for (args, 0..) |arg, i| {
std.debug.print("{}: {s}\n", .{ i, arg });
}
}
{#code_end#}
{#shell_samp#}$ wasmtime wasi_args.wasm 123 hello
0: wasi_args.wasm
1: 123
2: hello{#end_shell_samp#}
<p>A more interesting example would be extracting the list of preopens from the runtime.
This is now supported in the standard library via {#syntax#}std.fs.wasi.Preopens{#endsyntax#}:</p>
{#code_begin|exe|wasi_preopens#}
{#target_wasi#}
const std = @import("std");
const fs = std.fs;
{#code|wasi_preopens.zig#}
pub fn main() !void {
var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
const gpa = general_purpose_allocator.allocator();
var arena_instance = std.heap.ArenaAllocator.init(gpa);
defer arena_instance.deinit();
const arena = arena_instance.allocator();
const preopens = try fs.wasi.preopensAlloc(arena);
for (preopens.names, 0..) |preopen, i| {
std.debug.print("{}: {s}\n", .{ i, preopen });
}
}
{#code_end#}
{#shell_samp#}$ wasmtime --dir=. wasi_preopens.wasm
0: stdin
1: stdout
@@ -11573,21 +6843,8 @@ coding style.
<p>Every declaration is assigned a <strong>fully qualified
namespace</strong> by the compiler, creating a tree structure. Choose names based
on the fully-qualified namespace, and avoid redundant name segments.</p>
{#code_begin|exe|redundant_fqn#}
const std = @import("std");
{#code|redundant_fqn.zig#}
pub const json = struct {
pub const JsonValue = union(enum) {
number: f64,
boolean: bool,
// ...
};
};
pub fn main() void {
std.debug.print("{s}\n", .{@typeName(json.JsonValue)});
}
{#code_end#}
<p>In this example, "json" is repeated in the fully-qualified namespace. The solution
is to delete <code>Json</code> from <code>JsonValue</code>. In this example we have
an empty struct named <code>json</code> but remember that files also act