diff --git a/doc/langref.html.in b/doc/langref.html.in
index a5dfa5c9278a..af3e59b3fd6e 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -389,8 +389,8 @@ pub fn main() !void {
with the {#syntax#}!void{#endsyntax#} return type. This return type is known as an {#link|Error Union Type#}.
This syntax tells the Zig compiler that the function will either return an
error or a value. An error union type combines an {#link|Error Set Type#} and any other data type
- (e.g. a {#link|Primitive Type|Primitive Types#} or a user-defined type such as a {#link|struct#}, {#link|enum#}, or {#link|union#}).
- The full form of an error union type is
+ (e.g. a {#link|Primitive Type|Primitive Types#} or a {#link|user-defined type|Containers#} such as a
+ {#link|struct#}, {#link|enum#}, or {#link|union#}). The full form of an error union type is
<error set type>{#syntax#}!{#endsyntax#}<any data type>. In the code
sample, the error set type is not explicitly written on the left side of the {#syntax#}!{#endsyntax#} operator.
When written this way, the error set type is an {#link|inferred error set type|Inferred Error Sets#}. The
@@ -426,7 +426,7 @@ pub fn main() !void {
purposely written to show how to perform {#link|string|String Literals and Unicode Code Point Literals#}
substitution in the {#syntax#}print{#endsyntax#} function. The curly-braces inside of the first argument
are substituted with the compile-time known value inside of the second argument
- (known as an {#link|anonymous struct literal|Anonymous Struct Literals#}). The \n
+ (known as a tuple which is type of an {#link|anonymous struct literal|Anonymous Struct Literals#}). The \n
inside of the double-quotes of the first argument is the {#link|escape sequence|Escape Sequences#} for the
newline character. The {#link|try#} expression evaluates the result of {#syntax#}stdout.print{#endsyntax#}.
If the result is an error, then the {#syntax#}try{#endsyntax#} expression will return from
@@ -456,97 +456,21 @@ pub fn main() void {
- zig test is a tool that can be used to quickly build and run Zig code - to make sure behavior meets expectations. {#syntax#}@import("builtin").is_test{#endsyntax#} - is available for code to detect whether the current build is a test build. -
- {#code_begin|test|detect_test#} -const std = @import("std"); -const builtin = @import("builtin"); -const expect = std.testing.expect; - -test "builtin.is_test" { - try expect(builtin.is_test); -} - {#code_end#} -- Zig has lazy top level declaration analysis, which means that if a function is not called, - or otherwise used, it is not analyzed. This means that there may be an undiscovered - compile error in a function because it is never called. -
- {#code_begin|test|unused_fn#} -fn unused() i32 { - return "wrong return type"; -} -test "unused function" { } - {#code_end#} -- Note that, while in {#link|Debug#} and {#link|ReleaseSafe#} modes, {#link|unreachable#} emits a - call to {#link|@panic#}, in {#link|ReleaseFast#} and {#link|ReleaseSmall#} modes, it is really - undefined behavior. The implementation of {#syntax#}std.debug.assert{#endsyntax#} is as - simple as: -
- {#code_begin|syntax|assert#} -pub fn assert(ok: bool) void { - if (!ok) unreachable; -} - {#code_end#} -- This means that when testing in ReleaseFast or ReleaseSmall mode, {#syntax#}assert{#endsyntax#} - is not sufficient to check the result of a computation: -
- {#code_begin|syntax|assert_release_fast_mode#} -const std = @import("std"); -const assert = std.debug.assert; - -test "assert in release fast mode" { - assert(false); -} - {#code_end#} -- When compiling this test in {#link|ReleaseFast#} mode, it invokes unchecked - {#link|Undefined Behavior#}. Since that could do anything, this documentation - cannot show you the output. -
-- Better practice for checking the output when testing is to use {#syntax#}std.testing.expect{#endsyntax#}: -
- {#code_begin|test_err|test "expect in release fast mode"... FAIL (TestUnexpectedResult)#} - {#code_release_fast#} -const std = @import("std"); -const expect = std.testing.expect; - -test "expect in release fast mode" { - try expect(false); -} - {#code_end#} -See the rest of the {#syntax#}std.testing{#endsyntax#} namespace for more available functions.
-- zig test has a few command line parameters which affect the compilation. See - zig --help for a full list. The most interesting one is --test-filter [text]. - This makes the test build only include tests whose name contains the supplied filter text. - Again, thanks to lazy analysis, this can allow you to narrow a build to only a few functions in - isolation. -
- {#header_close#} {#header_open|Comments#} - {#code_begin|test|comments#} -const expect = @import("std").testing.expect; + {#code_begin|exe|comments#} +const print = @import("std").debug.print; -test "comments" { +pub fn main() void { // Comments in Zig start with "//" and end at the next LF byte (end of line). - // The below line is a comment, and won't be executed. + // The line below is a comment and won't be executed. - //expect(false); + //print("Hello?", .{}); - const x = true; // another comment - try expect(x); + print("Hello, world!\n", .{}); // another comment } {#code_end#}
- There are no multiline comments in Zig (e.g. like /* */
+ There are no multiline comments in Zig (e.g. like /* */
comments in C). This helps allow Zig to have the property that each line
of code can be tokenized out of context.
User documentation that doesn't belong to whatever immediately follows it, like package-level documentation, goes - in top-level doc comments. A top-level doc comment is one that + in {#link|top-level|Top-Level#} doc comments. A container doc comment is one that begins with two slashes and an exclamation point: {#syntax#}//!{#endsyntax#}.
- {#code_begin|syntax|tldoc_comments#} + {#code_begin|syntax|container_doc_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. @@ -896,24 +820,25 @@ pub fn main() void { in recent versions of the Unicode specification (as of Unicode 13.0). In Zig, a Unicode code point literal corresponds to the Unicode definition of a code point. - {#code_begin|test|string_literals_test#} -const expect = @import("std").testing.expect; -const mem = @import("std").mem; + {#code_begin|exe|string_literals#} +const print = @import("std").debug.print; +const mem = @import("std").mem; // will be used to compare bytes -test "string literals" { +pub fn main() void { const bytes = "hello"; - try expect(@TypeOf(bytes) == *const [5:0]u8); - try expect(bytes.len == 5); - try expect(bytes[1] == 'e'); - try expect(bytes[5] == 0); - try expect('e' == '\x65'); - try expect('\u{1f4a9}' == 128169); - try expect('💯' == 128175); - try expect(mem.eql(u8, "hello", "h\x65llo")); - try expect("\xff"[0] == 0xff); // non-UTF-8 strings are possible with \xNN notation. -} - {#code_end#} - {#see_also|Arrays|Zig Test|Source Encoding#} + print("{s}\n", .{@typeName(@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("{}\n", .{mem.eql(u8, "hello", "h\x65llo")}); // true + print("0x{x}\n", .{"\xff"[0]}); // non-UTF-8 strings are possible with \xNN notation. + print("{u}\n", .{'⚡'}); +} + {#code_end#} + {#see_also|Arrays|Source Encoding#} {#header_open|Escape Sequences#}{#syntax#}x() x[] x.y x.* x.?
-a!b
-x{}
-!x -x -%x ~x &x ?x
-* / % ** *% ||
-+ - ++ +% -%
-<< >>
-& ^ | orelse catch
-== != < > <= >=
-and
-or
-= *= /= %= += -= <<= >>= &= ^= |={#endsyntax#}
+ {#header_open|Precedence#}
+ {#syntax#}x() x[] x.y x.* x.?
+a!b
+x{}
+!x -x -%x ~x &x ?x
+* / % ** *% ||
++ - ++ +% -%
+<< >>
+& ^ | orelse catch
+== != < > <= >=
+and
+or
+= *= /= %= += -= <<= >>= &= ^= |={#endsyntax#}
+ {#header_close#}
+ {#header_close#}
+ {#header_open|Containers#}
+ + In the context of the Zig programming language, a container is a generic term used for + describing a language feature that groups related data and functionality. +
+Containers are used for creating user-defined types.
+ {#header_open|Types of Containers#} ++ Containers have a default, extern, or packed memory layout. See specific container documentation + for more information. +
+ {#see_also|extern struct|packed struct|extern enum|extern union|packed union#} + {#header_close#} + {#header_open|Nesting Containers#} ++ Containers can be declared inside one another. +
+ {#code_begin|exe|nested_containers#} +const print = @import("std").debug.print; + +const zig = struct { + const community = struct { + const Ziguana = union(enum) { + yes: HowMuchEnum, // Can reference identifiers in the same container + no: WhyNoEnum, + + const HowMuchEnum = enum { + beginner, + intermediate, + professional, + wizard, + }; + + const WhyNoEnum = enum { + not_yet, + soon, + later, + }; + }; + + const Contribute = union(enum) { + start_project, + spread_word, + translate, + help_learn, + work_on_issues, + give: foundation.Sponsor, // Can reference identifiers in other containers + }; + }; + + const foundation = struct { + const Board = struct { + president: community.Ziguana, + secretary: community.Ziguana, + treasurer: community.Ziguana, + }; + + const Sponsor = enum { + monetary, + infrastructure, + }; + }; +}; + +pub fn main() void { + const friendo = zig.community.Ziguana{ .yes = .wizard }; + const soon_friendo = zig.community.Ziguana{ .no = .soon }; + const help_out = zig.community.Contribute.spread_word; + + print("Andrew is a {}\n", .{friendo}); + print("Gopher is a {}\n", .{soon_friendo}); + print("Ziggy helps out by spreading the word, right? {}", .{help_out == .spread_word}); +} + {#code_end#} + {#header_close#} + {#header_open|Container Namespace#} ++ When a container is given a name, it provides a namespace for the identifiers it contains. Identifiers + that are namespaced are unique. +
++ There are three ways to name a container: +
++ Otherwise, the container is considered to be anonymous. +
+ {#code_begin|exe|container_namespace#} +const std = @import("std"); + +// The following three functions return a `type` of container. +// The containers are given the name of the function. +fn StructFun(comptime T: type) type { + return struct { f: T }; +} + +fn EnumFun(comptime T: type) type { + return enum(T) { f }; +} + +fn UnionFun(comptime T: type) type { + return union { f: T }; +} + +pub fn main() void { + // The name of an import + std.debug.print("As import: {s}\n\n", .{@typeName(@import("hello.zig"))}); + + // In this example, containers are declared in three ways: + // 1. Declared and stored in a constant local identifier + // 2. By using a function to declare the container at compile-time + // 3. By declaring the container anonymously + // + // The name of the containers are printed using the code below. + + const S = struct { f: i32 }; + const sFun = StructFun(i32); + std.debug.print("As identifier: {s}\n", .{@typeName(S)}); + std.debug.print("As function: {s}\n", .{@typeName(sFun)}); + std.debug.print("As anonymous: {s}\n\n", .{@typeName(struct { f: i32 })}); + + const E = enum { f }; + const eFun = EnumFun(u8); + std.debug.print("As identifier: {s}\n", .{@typeName(E)}); + std.debug.print("As function: {s}\n", .{@typeName(eFun)}); + std.debug.print("As anonymous: {s}\n\n", .{@typeName(enum { f })}); + + const U = union { f: bool }; + const uFun = UnionFun(bool); + std.debug.print("As identifier: {s}\n", .{@typeName(U)}); + std.debug.print("As function: {s}\n", .{@typeName(uFun)}); + std.debug.print("As anonymous: {s}\n\n", .{@typeName(union { f: bool })}); +} + {#code_end#} + {#header_open|usingnamespace#} ++ {#syntax#}usingnamespace{#endsyntax#} is a declaration that mixes all the public + declarations of the operand, which must be a {#link|struct#}, {#link|union#}, {#link|enum#}, + or {#link|opaque#}, into the immediate container's namespace: +
+ {#code_begin|exe|usingnamespace#} +pub fn main() void { + const S = struct { + usingnamespace @import("std"); + }; + S.debug.print("S contains std's declarations", .{}); +} + {#code_end#} +
+ {#syntax#}usingnamespace{#endsyntax#} has an important use case when organizing the public
+ API of a file or package. For example, one might have c.zig with all of the
+ {#link|C imports|Import from C Header File#}:
+
+ The above example demonstrates using {#syntax#}pub{#endsyntax#} to qualify the + {#syntax#}usingnamespace{#endsyntax#} additionally makes the imported declarations + {#syntax#}pub{#endsyntax#}. This can be used to forward declarations, giving precise control + over what declarations a given file exposes. +
+ {#header_close#} + {#header_close#} + {#header_open|Top-Level#} ++ The term top-level (also spelled "top level") refers to direct child of a container. +
++ Direct children of a container are called container level members, container level, + container members, top-level members, top-level or just members. +
+
+ Let's revisit the source code for the Hello, world! program again, as shown below. The
+ hello_again_again.zig source file is a container. Its first whole line of code
+ is its {#link|top-level variable declaration|Container Level Variables#}. The code starting at the third line until the end
+ of the file is the hello_again_again container's top-level {#link|function|Functions#} declaration.
+ In summary, the hello_again_again container has two top-level members.
+
+ Now, let's look at nesting containers and point out top-level language constructs. In the following code sample,
+ nested_containers_top_levels.zig is a container. The rest will be explained after the
+ code sample. Focus on the form of the code and ignore the meaning of the code.
+
+ There are three main types of top-level members. +
+The following are top-level declarations:
++ Note, {#link|Compile-Time Expressions#} and {#link|Variable Declarations|Variables#} can be located in places other than + the top-level position as well. +
+ + {#header_close#} + {#header_open|Container Fields#} ++ Container fields are also called variants or tags depending on the + {#link|type of container|Types of Containers#} being used. The number of fields a container can have + also depends on the type of container. +
++ When fields are declared in a container, they must be grouped together. No other type of top-level member + is allowed to be placed between container fields. Here are the three valid ways to arrange top-level + declarations and fields: +
+ {#code_begin|syntax|fields_first#} +field1: u8, +field2: u8, +const subcontainer1 = struct { + a: i32, +}; +const subcontainer2 = struct { + a: i32, +}; + {#code_end#} + {#code_begin|syntax|fields_between#} +const subcontainer1 = struct { + a: i32, +}; +field1: u8, +field2: u8, +const subcontainer2 = struct { + a: i32, +}; + {#code_end#} + {#code_begin|syntax|fields_last#} +const subcontainer1 = struct { + a: i32, +}; +const subcontainer2 = struct { + a: i32, +}; +field1: u8, +field2: u8 // comma is optional when field is the last top-level member + {#code_end#} + {#see_also|struct|enum|union#} + {#header_close#} + {#header_close#} + {#header_open|Top-Level Order Independent Declarations#} ++ Top-level declarations are order independent. They can be arranged in any order without changing + the functionality of the program/library. +
+ {#code_begin|exe|hello_top_level_independence#} +pub fn main() void { + print("Hello, world!\n", .{}); +} + +const print = @import("std").debug.print; + {#code_end#} +
+ The code sample above reordered the original hello_again.zig's top-level declarations.
+ The code compiles and runs the same.
+
+ Top-level declarations are resolved at compile-time. When a top-level member uses an identifier, the compiler verifies + that the identifier has been declared. +
+Use of undeclared identifiers is a compile error caused by a top-level member being compile-time resolved.
+ {#code_begin|exe_build_err#} +fn deepThought() i32 { + // Compile error because `ultimateQuestion` is undeclared + const question = ultimateQuestion(); +} + {#code_end#} +Not using a declared identifier is a compile error caused by a top-level member being compile-time resolved.
+ {#code_begin|exe_build_err#} +fn ohDear() i32 { + // Compile error because `answer` is unused + const answer: i32 = 4 * 13; +} + {#code_end#} + {#header_close#} + {#header_open|Lazy Top-Level Declaration Analysis#} ++ Zig does lazy, top-level declaration analysis, which means that if a top-level member is not called, + or otherwise used, it is not analyzed with exception to the {#link|Top-Level Compile-Time Resolution#} rules. +
+ {#code_begin|test|unused_fn#} +// Although this function returns the wrong type, no compile error +// because this function is never called. +fn unused() i32 { + return "wrong return type"; +} +test "unused function" { } + {#code_end#} + {#header_close#} + {#header_open|Self-Referential Containers#} ++ When a container is given a name, it can refer to itself directly or indirectly unless + it causes an infinite recursion. To avoid infinite recursion, use a {#link|pointer|Pointers#}, or + pointer-like type. +
+ {#code_begin|exe|self_referencing_containers#} +const print = @import("std").debug.print; + +fn LinkedList(comptime T: type) type { + return struct { + pub const Node = struct { // Node is declared at the top-level of a container + prev: ?*Node, // So, it can self-reference + next: ?*Node, // Node indirectly self-references using a pointer + data: T, + }; + + first: ?*Node, + last: ?*Node, + len: usize, + }; +} + +const Choices = enum { + B, + C, + Zig, + const default = Choices.Zig; // direct self-reference +}; + +const JsonType = union(enum) { + object: ?struct { k: u32 }, + array: []const JsonType, // indirect self-reference using a slice + string: []const u8, + number: u32, + boolean: bool, +}; + +pub fn main() void { + // A doubly linked list with nodes storing i32 typed data + const ListOfInts = LinkedList(i32); + var node = ListOfInts.Node{ + .prev = null, + .next = null, + .data = 42, + }; + var list = ListOfInts{ + .first = &node, + .last = &node, + .len = 1, + }; + print("{d}\n", .{list.first.?.data}); + + // Choices + const choice = Choices.default; + print("You chose Zig: {}\n", .{choice == .Zig}); + + // Simulate a JSON array + const jsonArray = &[_]JsonType{ + JsonType{ .number = 42 }, + JsonType{ .array = &[_]JsonType{ + JsonType{ .boolean = false }, + JsonType{ .boolean = true }, + } }, + }; + print("Gotta go fast: {}", .{jsonArray[1].array[1].boolean}); +} + {#code_end#} ++ Containers can only self-reference if they are named by a top-level declaration. + The compiler will give an error when trying to self-reference containers from + non-top-level declarations: +
+ {#code_begin|exe_build_err|self_reference_error#} +const print = @import("std").debug.print; + +fn deepThought() i32 { + const Node = struct { // Node is not named by a top-level declaration + child: ?*Node, // This container cannot self-reference + data: i32, + }; + var leaf = Node{ + .child = null, + .data = 42, + }; + const root = Node{ + .child = &leaf, + .data = 0, + }; + return root.child.?.data; +} + +fn ultimateChoice() bool { + const Choices = enum { // This is not named by a top-level declaration + B, + C, + Zig, + const default = Choices.Zig; // This container cannot self-reference + }; + + return .Zig == Choices.default; +} + +pub fn main() void { + print("The ultimate answer: {any}", .{deepThought()}); + print("You chose Zig: {}", .{ultimateChoice()}); +} + {#code_end#} ++ To have the same result as self-referencing, the code sample above can use the {#syntax#}@This(){#endsyntax#} + {#link|built-in function|@This#}: +
+ {#code_begin|exe|simulated_self_reference#} +const print = @import("std").debug.print; + +fn deepThought() i32 { + const Node = struct { // Node is not declared in a container + const Self = @This(); // Assign this container the name `Self` + child: ?*Self, + data: i32, + }; + var leaf = Node{ + .child = null, + .data = 42, + }; + const root = Node{ + .child = &leaf, + .data = 0, + }; + return root.child.?.data; +} + +fn ultimateChoice() bool { + const Choices = enum { + B, + C, + Zig, + const default = @This().Zig; // simulated self-reference + }; + + return .Zig == Choices.default; +} + +pub fn main() void { + print("The ultimate answer: {any}", .{deepThought()}); + print("You chose Zig: {}", .{ultimateChoice()}); +} + {#code_end#} + {#see_also|Arrays|Pointers|Slices|Optionals#} + {#header_close#} + {#header_close#} + {#header_close#} + + {#header_open|Zig Test#} ++ Code written within one or more {#syntax#}test{#endsyntax#} declarations can help to + ensure behavior meets expectations: +
+ {#code_begin|test|introducing_zig_test#} +const std = @import("std"); + +test "expect addOne adds one to 41 correctly" { + + // 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); +} + +/// The function `addOne` adds one to the number given as its argument. +fn addOne(number: i32) i32 { + return number + 1; +} + {#code_end#} ++ zig test is a tool that creates and runs a test build. By default, it builds and runs an + executable program using the default test runner provided by the {#link|Zig Standard Library#} + as its main entry point. During the build, {#syntax#}test{#endsyntax#} declarations found while + {#link|resolving|Root Source File#} the given Zig source file are included for the default test runner + to run and report on. +
+ ++ The shell output shown above displays the line Test [1/1] test "expect addOne adds one to 41 correctly".... + This line was printed to standard error (stderr) by the test runner program. (When the test runner program's + standard error is redirected to the terminal, these lines are cleared when a test succeeds.) + Lines like this indicate which test, out of the total number of tests, is being run. In this case, [1/1] + indicates that the first test, out of a total of one test, is being run. The next line, All 1 tests passed., + is also printed to standard error. It indicates the total number of tests that have passed. +
+
+ The code sample in introducing_zig_test.zig tests the {#link|function|Functions#}
+ {#syntax#}addOne{#endsyntax#} to ensure that it correctly adds one to {#syntax#}41{#endsyntax#}.
+ From this test's perspective, the {#syntax#}addOne{#endsyntax#} function is said to be
+ code under test.
+
+ Test declarations contain the {#link|keyword|Keyword Reference#} {#syntax#}test{#endsyntax#} followed by an + optional name written between double-quotation marks followed by a {#link|block|blocks#} containing test code. +
++ By convention, non-named tests should be used when referencing other tests. Non-named tests are run regardless + of the {#link|--test-filter|Skip Tests#} command-line parameter. +
++ Tests are similar to {#link|Functions#}: they have a return type and a block of code. The implicit + return type of {#syntax#}test{#endsyntax#} is the {#link|Error Union Type#} {#syntax#}anyerror!void{#endsyntax#}. + When a Zig source file is not built using the zig test tool, the tests are omitted from the build. +
++ Test declarations can be written in the same file where code under test is written or in a separate Zig source file. + Since test declarations are {#link|top-level|Top Level#} declarations, they are order-independent and can + be written before or after the code under test. +
+ {#see_also|The Global Error Set#} + {#header_close#} + {#header_open|Nested Test Declarations#} ++ Top-level test declarations are {#link|lazily analyzed|Lazy Top-Level Declaration Analysis#}. + When the zig test tool is building a test runner, only resolved {#syntax#}test{#endsyntax#} + declarations are included in the build. Initially, the given Zig source file's top-level declarations are + resolved. Unless nested containers are referenced, the nested containers' tests will not be resolved. +
++ The code sample below uses the {#syntax#}std.testing.refAllDecls(@This()){#endsyntax#} function call to + reference all of the {#link|Containers#} that are in the file. This includes the test declarations + located in imported, local Zig source files. The code sample also shows an alternative way to reference + containers using the {#syntax#}_ = C;{#endsyntax#} syntax. This syntax tells the compiler to ignore the + result of the expression on the right side of the assignment operator. +
+ {#code_begin|test|testdecl_container_top_level#} +const std = @import("std"); +const expect = std.testing.expect; + +// Locally imported source files will be tested when referenced +const local_import = @import("introducing_zig_test.zig"); + +test { + // To run nested-containers' tests, either, call `refAllDecls` which will + // reference all declarations located in the given argument. + // `@This()` is a builtin function that returns the innermost container it is called from. + // In this example, the innermost container is this file (implicitly a struct). + std.testing.refAllDecls(@This()); + + // or, reference each container individually from a top-level test declaration. + // The `_ = C;` syntax is a no-op reference to the identifier `C`. + _ = S; + _ = E; + _ = U; + _ = O; + + // Alternative to `refAllDecls`, other local source files containing tests can + // also be referenced like this + _ = @import("introducing_zig_test.zig"); +} + +const S = struct { + test "S demo test" { + try expect(true); + } +}; + +const E = enum { + V, + + test "E demo test" { + try expect(true); + } +}; + +const U = union { + s: US, // US is referenced here; therefore, its test will run + + const US = struct { + test "U.US demo test" { + // This test is a top-level test declaration for the struct. + // The struct is nested (declared) inside of a union. + try expect(true); + } + }; + + test "U demo test" { + try expect(true); + } +}; + +const O = opaque { + test "O demo test" { + try expect(true); + } +}; + {#code_end#} + {#header_close#} + {#header_open|Test Failure#} ++ The default test runner checks for an {#link|error|Errors#} returned from a test. + When a test returns an error, the test is considered a failure and the failure will be reported after all + test have been run. When a test fails, the default test runner will output the test that failed along with the + {#link|Error Return Traces#}. +
+ {#code_begin|test_err#} +const std = @import("std"); + +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#} ++ If a {#syntax#}test{#endsyntax#} returns the error {#syntax#}error.SkipZigTest{#endsyntax#}, the + default test runner will consider the test as being skipped. After all of the tests are complete, + the number of skipped tests is reported. +
+ {#code_begin|test#} +test "this will be skipped" { + return error.SkipZigTest; +} + {#code_end#} ++ The default test runner also skips tests containing a {#link|suspend point|Async Functions#} and the + test is running using the default, blocking IO mode. + (The evented IO mode is enabled using the --test-evented-io command line parameter.) +
+ {#code_begin|test|async_skip#} +const std = @import("std"); + +// This test is skipped because a suspend point exists +// and the `io_mode`` is set to blocking (the default). +test "async skip test" { + var frame = async func(); + const result = await frame; + try std.testing.expect(result == 1); +} + +fn func() i32 { + suspend { + resume @frame(); + } + return 1; +} + {#code_end#} ++ In the code sample above, the test would not be skipped in blocking IO mode if the {#syntax#}nosuspend{#endsyntax#} + keyword was used (see {#link|Async and Await#}). +
++ Another way to skip tests is to filter them out by using the zig test command line parameter + --test-filter [text]. This makes the test build only include tests whose name contains the + supplied filter text. Note, non-named tests are run even when using the --test-filter [text] + command line parameter. +
+ {#header_close#} + {#header_open|Report Memory Leaks#} ++ When code allocates {#link|Memory#} using the {#link|Zig Standard Library#}'s testing allocator, + {#syntax#}std.testing.allocator{#endsyntax#}, the default test runner will report any leaks that are + found from using this allocator: +
+ {#code_begin|test_err#} +const std = @import("std"); + +test "detect leak" { + var list = std.ArrayList(u8).init(std.testing.allocator); + // missing `defer list.deinit();` + try list.append('Z'); + try list.append('i'); + try list.append('g'); + + try std.testing.expect(list.items.len == 3); +} + {#code_end#} + {#see_also|defer|Memory#} + {#header_close#} + {#header_open|Detecting Test Build#} ++ To detect that code is being run by the zig test tool, use the + {#link|compile variable|Compile Variables#} {#syntax#}@import("builtin").is_test{#endsyntax#}. +
+ {#code_begin|test|detect_test#} +const std = @import("std"); +const builtin = @import("builtin"); +const expect = std.testing.expect; + +test "builtin.is_test" { + try expect(builtin.is_test); + try expect(isATest()); +} + +fn isATest() bool { + return builtin.is_test; +} + {#code_end#} + {#header_close#} + {#header_open|Test Output and Logging#} ++ The default test runner and the Zig Standard Library's testing {#link|namespace|Container Namespace#} + output messages to standard error. +
+ {#header_close#} + {#header_open|The Testing Namespace#} +
+ The Zig Standard Library's testing {#link|namespace|Container Namespace#} contains useful functions to help
+ you build tests. A few are listed here:
+
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.
+ {#header_close#} + {#header_open|Test Tool Documentation#} ++ zig test has a few command line parameters which affect the compilation. + See zig test --help for a full list. +
+ {#header_close#} + {#header_close#} + {#header_open|Variables#} ++ A variable is a unit of {#link|Memory#} storage. +
++ Variables are never allowed to shadow identifiers from an outer scope. +
++ It is generally preferable to use {#syntax#}const{#endsyntax#} rather than + {#syntax#}var{#endsyntax#} when declaring a variable. This causes less work for both + humans and computers to do when reading code, and creates more optimization opportunities. +
+ {#header_open|Container Level Variables#} ++ {#link|Container level|Top-Level#} variables have static lifetime and are order-independent and lazily analyzed. + The initialization value of container level variables is implicitly + {#link|comptime#}. If a container level variable is {#syntax#}const{#endsyntax#} then its value is + {#syntax#}comptime{#endsyntax#}-known, otherwise it is runtime-known. +
+ {#code_begin|test|container_level_variables#} +var y: i32 = add(10, x); +const x: i32 = add(12, 34); + +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#} ++ {#link|Container level|Top-Level#} variables may be declared inside a {#link|struct#}, {#link|union#}, or {#link|enum#}: +
+ {#code_begin|test|namespaced_container_level_variable#} +const std = @import("std"); +const expect = std.testing.expect; + +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#} ++ It is also possible to have local variables with static lifetime by using {#link|Containers#} inside functions. +
+ {#code_begin|test|static_local_variable#} +const std = @import("std"); +const expect = std.testing.expect; + +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#} ++ The {#syntax#}extern{#endsyntax#} keyword or {#link|@extern#} builtin function can be used to link against a variable that is exported + from another object. The {#syntax#}export{#endsyntax#} keyword or {#link|@export#} builtin function + can be used to make a variable available to other objects at link time. In both cases, + the type of the variable must be C ABI compatible. +
+ {#see_also|Exporting a C Library#} + {#header_close#} + + {#header_open|Thread Local Variables#} +A variable may be specified to be a thread-local variable using the + {#syntax#}threadlocal{#endsyntax#} keyword:
+ {#code_begin|test|tls#} +const std = @import("std"); +const assert = std.debug.assert; + +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#} ++ For {#link|Single Threaded Builds#}, all thread local variables are treated as regular {#link|Container Level Variables#}. +
++ Thread local variables may not be {#syntax#}const{#endsyntax#}. +
+ {#header_close#} + + {#header_open|Local Variables#} ++ Local variables occur inside {#link|Functions#}, {#link|comptime#} blocks, and {#link|@cImport#} blocks. +
++ When a local variable is {#syntax#}const{#endsyntax#}, it means that after initialization, the variable's + value will not change. If the initialization value of a {#syntax#}const{#endsyntax#} variable is + {#link|comptime#}-known, then the variable is also {#syntax#}comptime{#endsyntax#}-known. +
++ A local variable may be qualified with the {#syntax#}comptime{#endsyntax#} keyword. This causes + the variable's value to be {#syntax#}comptime{#endsyntax#}-known, and all loads and stores of the + variable to happen during semantic analysis of the program, rather than at runtime. + All variables declared in a {#syntax#}comptime{#endsyntax#} expression are implicitly + {#syntax#}comptime{#endsyntax#} variables. +
+ {#code_begin|test|comptime_vars#} +const std = @import("std"); +const expect = std.testing.expect; + +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|Arrays#} {#code_begin|test|arrays#} const expect = @import("std").testing.expect; @@ -2612,7 +3362,6 @@ test "null terminated slice" { {#see_also|Sentinel-Terminated Pointers|Sentinel-Terminated Arrays#} {#header_close#} {#header_close#} - {#header_open|struct#} {#code_begin|test|structs#} // Declare a struct. @@ -4376,17 +5125,16 @@ test "errdefer unwinding" { {#header_close#} {#header_open|unreachable#}
- In {#syntax#}Debug{#endsyntax#} and {#syntax#}ReleaseSafe{#endsyntax#} mode, and when using zig test,
- {#syntax#}unreachable{#endsyntax#} emits a call to {#syntax#}panic{#endsyntax#} with the message reached unreachable code.
+ In {#link|Debug#} mode, {#link|ReleaseSafe#} mode, and when using {#link|Zig Test#} in any mode,
+ {#syntax#}unreachable{#endsyntax#} emits a call to {#link|@panic#} with the message reached unreachable code.
- In {#syntax#}ReleaseFast{#endsyntax#} mode, the optimizer uses the assumption that {#syntax#}unreachable{#endsyntax#} code - will never be hit to perform optimizations. However, zig test even in {#syntax#}ReleaseFast{#endsyntax#} mode - still emits {#syntax#}unreachable{#endsyntax#} as calls to {#syntax#}panic{#endsyntax#}. + In {#link|ReleaseFast#} mode and {#link|ReleaseSmall#} mode, reaching {#syntax#}unreachable{#endsyntax#} is + {#link|undefined behavior|Reaching Unreachable Code#}.
{#header_open|Basics#} {#code_begin|test|test_unreachable#} -// unreachable is used to assert that control flow will never happen upon a +// `unreachable` is used to assert that control flow will never happen upon a // particular location: test "basic math" { const x = 1; @@ -5917,42 +6665,6 @@ test "@intToPtr for pointer to zero bit type" { {#header_close#} - {#header_open|usingnamespace#} -- {#syntax#}usingnamespace{#endsyntax#} is a declaration that mixes all the public - declarations of the operand, which must be a {#link|struct#}, {#link|union#}, {#link|enum#}, - or {#link|opaque#}, into the namespace: -
- {#code_begin|test|usingnamespace#} -test "using std namespace" { - const S = struct { - usingnamespace @import("std"); - }; - try S.testing.expect(true); -} - {#code_end#} -
- {#syntax#}usingnamespace{#endsyntax#} has an important use case when organizing the public
- API of a file or package. For example, one might have c.zig with all of the
- {#link|C imports|Import from C Header File#}:
-
- The above example demonstrates using {#syntax#}pub{#endsyntax#} to qualify the - {#syntax#}usingnamespace{#endsyntax#} additionally makes the imported declarations - {#syntax#}pub{#endsyntax#}. This can be used to forward declarations, giving precise control - over what declarations a given file exposes. -
- {#header_close#} - {#header_open|comptime#}@@ -6282,7 +6994,7 @@ test "fibonacci" {
- At container level (outside of any function), all expressions are implicitly + At the {#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:
@@ -6360,8 +7072,8 @@ fn List(comptime T: type) type { the anonymous struct.- To keep the language small and uniform, all aggregate types in Zig are anonymous. To give a type - a name, we assign it to a constant: + To keep the language small and uniform, all {#link|container types|Types of Containers#} in Zig are anonymous by default. + To give a container type a {#link|name|Container Namespace#}, we assign it to a constant:
{#code_begin|syntax#} const Node = struct { @@ -6370,10 +7082,7 @@ const Node = struct { }; {#code_end#}- This works because all top level declarations are order-independent, and as long as there isn't - an actual infinite regression, values can refer to themselves, directly or indirectly. In this case, - {#syntax#}Node{#endsyntax#} refers to itself as a pointer, which is not actually an infinite regression, so - it works fine. + This works because container types can {#link|self-reference|Self-Referential Containers#}.
{#header_close#} {#header_open|Case Study: print in Zig#} @@ -6735,7 +7444,7 @@ volatile ( {#header_open|Global Assembly#}- When an assembly expression occurs in a container level {#link|comptime#} block, this is + When an assembly expression occurs in a {#link|container|Containers#} level {#link|comptime#} block, this is global assembly.
@@ -9927,8 +10636,7 @@ test "string literal to constant slice" { dereferencing them becomes unchecked {#link|Undefined Behavior#}.
- {#syntax#}var{#endsyntax#} declarations at the top level or in {#link|struct#} declarations are stored in the global - data section. + {#link|Top-Level#} {#syntax#}var{#endsyntax#} declarations are stored in the global data section.
The location of memory allocated with {#syntax#}allocator.alloc{#endsyntax#} or @@ -10058,8 +10766,8 @@ const separator = if (builtin.os.tag == builtin.Os.windows) '\\' else '/';
TODO: pub fn main
TODO: pub fn panic
TODO: if linking with libc you can use export fn main
-TODO: order independent top level declarations
-TODO: lazy analysis
+{#link|Top-Level Order Independent Declarations#}
+{#link|Lazy Top-Level Declaration Analysis#}
TODO: using comptime { _ = @import() }
{#header_close#} {#header_open|Zig Build System#} @@ -10339,10 +11047,9 @@ pub fn main() void { can be called so long as the linker is aware of the compiled function.- {#syntax#}@compileError{#endsyntax#} is used when top-level definitions (global variables, - function prototypes, macros) cannot be translated or demoted. Since Zig uses lazy analysis for - top-level declarations, untranslatable entities will not cause a compile error in your code unless - you actually use them. + {#syntax#}@compileError{#endsyntax#} is used when {#link|top-level|Top-Level#} definitions cannot be + translated or demoted. Since Zig uses {#link|Lazy Top-Level Declaration Analysis#}, untranslatable entities will + not cause a compile error in your code unless you actually use them.
{#see_also|opaque|extern|@compileError#} {#header_close#} @@ -10889,7 +11596,7 @@ coding style. If {#syntax#}x{#endsyntax#} is a {#syntax#}type{#endsyntax#} then {#syntax#}x{#endsyntax#} should be {#syntax#}TitleCase{#endsyntax#}, unless it is a {#syntax#}struct{#endsyntax#} with 0 fields and is never meant to be instantiated, - in which case it is considered to be a "namespace" and uses {#syntax#}snake_case{#endsyntax#}. + in which case it is considered to be a {#link|namespace|Container Namespace#} and uses {#syntax#}snake_case{#endsyntax#}.
File names fall into two categories: types and namespaces. If the file
- (implicitly a struct) has top level fields, it should be named like any
+ (implicitly a struct) has {#link|Top-Level#} fields, it should be named like any
other struct with fields using TitleCase. Otherwise,
it should use snake_case. Directory names should be
snake_case.
@@ -11399,7 +12106,7 @@ fn readU32Be() u32 {}
{#syntax#}pub{#endsyntax#}
{#syntax#}test{#endsyntax#}
{#syntax#}usingnamespace{#endsyntax#}