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 {

{#see_also|Values|@import|Errors|Root Source File|Source Encoding#} {#header_close#} - {#header_open|Zig Test#} -

- 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.

@@ -582,13 +506,13 @@ const Timestamp = struct { in the middle of an expression, or just before a non-doc comment.

{#header_close#} - {#header_open|Top-Level Doc Comments#} + {#header_open|Container Doc Comments#}

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#}
@@ -986,7 +911,7 @@ const hello_world_in_c = {#header_close#} {#header_open|Assignment#}

Use the {#syntax#}const{#endsyntax#} keyword to assign a value to an identifier:

- {#code_begin|test_err|cannot assign to constant#} + {#code_begin|exe_build_err|constant_identifier_cannot_change#} const x = 1234; fn foo() void { @@ -997,26 +922,26 @@ fn foo() void { y += 1; } -test "assignment" { +pub fn main() void { foo(); } {#code_end#}

{#syntax#}const{#endsyntax#} applies to all of the bytes that the identifier immediately addresses. {#link|Pointers#} have their own const-ness.

If you need a variable that you can modify, use the {#syntax#}var{#endsyntax#} keyword:

- {#code_begin|test|var_test#} -const expect = @import("std").testing.expect; + {#code_begin|exe|mutable_var#} +const print = @import("std").debug.print; -test "var" { +pub fn main() void { var y: i32 = 5678; y += 1; - try expect(y == 5679); + print("{d}", .{y}); } {#code_end#}

Variables must be initialized:

- {#code_begin|test_err#} -test "initialization" { + {#code_begin|exe_build_err|var_must_be_initialized#} +pub fn main() void { var x: i32; x = 1; @@ -1024,13 +949,13 @@ test "initialization" { {#code_end#} {#header_open|undefined#}

Use {#syntax#}undefined{#endsyntax#} to leave variables uninitialized:

- {#code_begin|test|undefined_test#} -const expect = @import("std").testing.expect; + {#code_begin|exe|undefined_test#} +const print = @import("std").debug.print; -test "init with undefined" { +pub fn main() void { var x: i32 = undefined; x = 1; - try expect(x == 1); + print("{d}", .{x}); } {#code_end#}

@@ -1047,166 +972,6 @@ test "init with undefined" { {#header_close#} {#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#} -

- Container 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#} -

- Container 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 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|Integers#} {#header_open|Integer Literals#} {#code_begin|syntax|integer_literals#} @@ -1963,21 +1728,1006 @@ const B = error{Two};
{#header_close#} - {#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_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#} + + {#header_close#} + {#header_open|Container Memory Layout#} +

+ 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: +

+
    +
  1. The file name (without extension) of a Zig source file is the name of the implicit struct it represents. See {#link|@import#}.
  2. +
  3. When a container is assigned to an identifier, the container's name is the identifier.
  4. +
  5. When a container is in the {#syntax#}return{#endsyntax#} expression of a function, the + container's name is the function's name.
  6. +
+

+ 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#}: +

+ {#syntax_block|zig|c.zig#} +pub usingnamespace @cImport({ + @cInclude("epoxy/gl.h"); + @cInclude("GLFW/glfw3.h"); + @cDefine("STBI_ONLY_PNG", ""); + @cDefine("STBI_NO_STDIO", ""); + @cInclude("stb_image.h"); +}); + {#end_syntax_block#} +

+ 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. +

+ {#code_begin|syntax|hello_again_again#} +const print = @import("std").debug.print; + +pub fn main() void { + print("Hello, world!\n", .{}); +} + {#code_end#} +

+ 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. +

+ {#code_begin|syntax|nested_containers_top_levels#} +const print = @import("std").debug.print; + +const S = struct { + pub fn sTopLevelFn() void { + print("Hello, world!\n", .{}); + } + + sTopLevelField: i32, + + const sTopLevelEnum = enum { + enumTopLevelField1, + enumTopLevelField2, + }; +}; + {#code_end#} + + {#header_open|Types of Top-Level#} +

+ There are three main types of top-level members. +

+
+
{#link|Container Documention Comments|Container Doc Comments#}
+
When Container Documentation Comments are used, they are always the first member of a container. + That is, they are put inside of the container before any of the other top-level members.
+
{#link|Top-Level Declarations#}
+
Top-Level Declarations introduce identifiers and functionality.
+
{#link|Container Fields#}
+
Container Fields are properties or variants of a container.
+
+ {#header_open|Top-Level Declarations#} +

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. +

+ {#header_close#} + {#header_open|Top-Level Compile-Time Resolution#} +

+ 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. +

+ {#header_open|Test Declarations#} +

+ 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: +

+ {#code_begin|test|testing_functions#} +const std = @import("std"); + +test "expect demo" { + try std.testing.expect(true); +} + +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#} +

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#}: -

- {#syntax_block|zig|c.zig#} -pub usingnamespace @cImport({ - @cInclude("epoxy/gl.h"); - @cInclude("GLFW/glfw3.h"); - @cDefine("STBI_ONLY_PNG", ""); - @cDefine("STBI_NO_STDIO", ""); - @cInclude("stb_image.h"); -}); - {#end_syntax_block#} -

- 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#}.
  • If {#syntax#}x{#endsyntax#} is callable, and {#syntax#}x{#endsyntax#}'s return type is @@ -10911,7 +11618,7 @@ coding style.

    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#}
    - The {#syntax#}pub{#endsyntax#} in front of a top level declaration makes the declaration available + The {#syntax#}pub{#endsyntax#} in front of a {#link|top-level declaration|Top-Level#} makes the declaration available to reference from a different file than the one it is declared in.