# Appendix A. Zig Syntax Summary

### Appendix A. Zig Syntax Summary

This appendix is a quick map of Zig syntax. It is not a grammar. The full Zig grammar is part of the official language reference. Zig 0.16 also keeps the language small enough that the grammar remains practical to read directly.

### A.1 Source Files

A Zig source file is a container. It contains declarations.

```zig
const std = @import("std");

const max_count = 100;

fn add(a: i32, b: i32) i32 {
    return a + b;
}

pub fn main() void {
    std.debug.print("{d}\n", .{add(1, 2)});
}
```

Declarations at file scope are order-independent.

```zig
pub fn main() void {
    f();
}

fn f() void {}
```

### A.2 Comments

```zig
// ordinary comment

/// documentation comment for the next declaration

//! documentation comment for the current container
```

Zig has no block comments.

### A.3 Names

```zig
const name = value;
var count: usize = 0;
```

A name may be public.

```zig
pub const version = 1;
pub fn run() void {}
```

A name may use `@"..."` when it is not a normal identifier.

```zig
const @"type" = 123;
```

### A.4 Imports

```zig
const std = @import("std");
const math = @import("math.zig");
```

An imported file is a struct-like namespace.

```zig
math.add(1, 2);
```

### A.5 Constants and Variables

```zig
const x = 10;
var y: i32 = 20;
```

A `const` binding cannot be assigned again.

```zig
const x = 1;
// x = 2; // error
```

A `var` binding can be assigned.

```zig
var n: i32 = 0;
n = n + 1;
```

### A.6 Basic Types

```zig
bool
void
noreturn
type
comptime_int
comptime_float

u8   i8
u16  i16
u32  i32
u64  i64
u128 i128
usize isize

f16
f32
f64
f80
f128
```

Integer types may also have explicit bit widths.

```zig
const small: u3 = 5;
const signed: i7 = -12;
```

### A.7 Literals

```zig
const a = 123;
const b = 0xff;
const c = 0b1010;
const d = 1.25;
const e = true;
const f = false;
const g = null;
const h = undefined;
```

Character and string literals:

```zig
const ch = 'A';
const s = "hello";
const nl = '\n';
```

Multiline strings use `\\`.

```zig
const text =
    \\first line
    \\second line
;
```

### A.8 Arrays

```zig
const a = [_]u8{ 1, 2, 3 };
const b: [3]u8 = .{ 1, 2, 3 };
```

Indexing:

```zig
const x = a[0];
```

Length:

```zig
const n = a.len;
```

Sentinel array:

```zig
const msg: [5:0]u8 = "hello".*;
```

### A.9 Slices

```zig
const a = [_]u8{ 1, 2, 3, 4 };
const s = a[1..3];
```

A slice has a pointer and a length.

```zig
s.ptr
s.len
```

Open-ended slice:

```zig
const t = a[2..];
```

### A.10 Strings

A string literal is a pointer to constant bytes.

```zig
const s = "hello";
```

Use `{s}` to print it.

```zig
std.debug.print("{s}\n", .{s});
```

Zig strings are byte sequences. Text encoding is a library concern.

### A.11 Pointers

Single-item pointer:

```zig
var x: i32 = 10;
const p: *i32 = &x;
p.* = 20;
```

Pointer to constant data:

```zig
const p: *const i32 = &x;
```

Many-item pointer:

```zig
const p: [*]u8 = buffer.ptr;
```

Optional pointer:

```zig
var p: ?*Node = null;
```

### A.12 Structs

```zig
const Point = struct {
    x: i32,
    y: i32,
};
```

Initialization:

```zig
const p = Point{ .x = 10, .y = 20 };
```

Field access:

```zig
const x = p.x;
```

Default field value:

```zig
const User = struct {
    id: u64,
    active: bool = true,
};
```

Methods are functions inside a struct.

```zig
const Point = struct {
    x: i32,
    y: i32,

    fn zero() Point {
        return .{ .x = 0, .y = 0 };
    }
};
```

### A.13 Enums

```zig
const Color = enum {
    red,
    green,
    blue,
};
```

Use:

```zig
const c = Color.red;
```

Inferred enum value:

```zig
const c: Color = .red;
```

Enum with integer tag type:

```zig
const Mode = enum(u8) {
    read = 1,
    write = 2,
};
```

### A.14 Unions

Plain union:

```zig
const Value = union {
    i: i32,
    f: f64,
};
```

Tagged union:

```zig
const Token = union(enum) {
    number: i64,
    name: []const u8,
    eof,
};
```

Switch on a tagged union:

```zig
switch (tok) {
    .number => |n| useNumber(n),
    .name => |s| useName(s),
    .eof => return,
}
```

### A.15 Optionals

```zig
var x: ?i32 = null;
x = 10;
```

Unwrap with `if`.

```zig
if (x) |value| {
    std.debug.print("{d}\n", .{value});
}
```

Use `orelse`.

```zig
const value = x orelse 0;
```

Force unwrap:

```zig
const value = x.?;
```

Use force unwrap only when null would be a programmer error.

### A.16 Errors

Error set:

```zig
const ParseError = error{
    Empty,
    InvalidDigit,
};
```

Error union:

```zig
fn parse() ParseError!i32 {
    return error.Empty;
}
```

Return success:

```zig
return 123;
```

Propagate error:

```zig
const n = try parse();
```

Handle error:

```zig
const n = parse() catch 0;
```

### A.17 Functions

```zig
fn add(a: i32, b: i32) i32 {
    return a + b;
}
```

No return value:

```zig
fn clear() void {}
```

Error return:

```zig
fn read() !usize {
    return error.EndOfStream;
}
```

Compile-time parameter:

```zig
fn max(comptime T: type, a: T, b: T) T {
    return if (a > b) a else b;
}
```

### A.18 Blocks

A block groups statements.

```zig
{
    const x = 1;
    _ = x;
}
```

A labeled block can yield a value.

```zig
const x = blk: {
    const a = 10;
    break :blk a + 1;
};
```

### A.19 `if`

```zig
if (x > 0) {
    positive();
} else {
    nonPositive();
}
```

`if` is an expression.

```zig
const sign = if (x < 0) -1 else 1;
```

Optional capture:

```zig
if (maybe) |value| {
    use(value);
} else {
    missing();
}
```

Error union capture:

```zig
if (parse()) |value| {
    use(value);
} else |err| {
    useError(err);
}
```

### A.20 `switch`

```zig
switch (x) {
    0 => zero(),
    1 => one(),
    else => other(),
}
```

Multiple values:

```zig
switch (ch) {
    'a', 'e', 'i', 'o', 'u' => vowel(),
    else => consonant(),
}
```

Range:

```zig
switch (n) {
    0...9 => digit(),
    else => other(),
}
```

Capture:

```zig
switch (tok) {
    .number => |n| use(n),
    else => {},
}
```

### A.21 `while`

```zig
var i: usize = 0;
while (i < 10) {
    i += 1;
}
```

Continue expression:

```zig
var i: usize = 0;
while (i < 10) : (i += 1) {
    use(i);
}
```

Optional loop:

```zig
while (next()) |item| {
    use(item);
}
```

Error union loop:

```zig
while (next()) |item| {
    use(item);
} else |err| {
    useError(err);
}
```

### A.22 `for`

```zig
for (items) |item| {
    use(item);
}
```

Index:

```zig
for (items, 0..) |item, i| {
    use(i, item);
}
```

Mutable pointer iteration:

```zig
for (&items) |*item| {
    item.* += 1;
}
```

Multiple sequences:

```zig
for (a, b) |x, y| {
    use(x, y);
}
```

### A.23 `break` and `continue`

```zig
while (true) {
    break;
}
```

```zig
while (condition()) {
    continue;
}
```

Labeled loop:

```zig
outer: while (true) {
    while (true) {
        break :outer;
    }
}
```

### A.24 `defer` and `errdefer`

`defer` runs at scope exit.

```zig
{
    lock();
    defer unlock();

    work();
}
```

`errdefer` runs only when the scope returns an error.

```zig
fn create() !*Thing {
    const p = try allocThing();
    errdefer freeThing(p);

    try initThing(p);
    return p;
}
```

### A.25 `comptime`

A `comptime` value is known during compilation.

```zig
fn Vec(comptime T: type, comptime n: usize) type {
    return struct {
        data: [n]T,
    };
}
```

Use:

```zig
const V3 = Vec(f32, 3);
```

Compile-time block:

```zig
comptime {
    _ = @import("std");
}
```

### A.26 Inline Loops

```zig
inline for (.{ u8, u16, u32 }) |T| {
    _ = T;
}
```

`inline` unrolls the loop at compile time.

### A.27 Anonymous Struct and Tuple Literals

Struct literal with known type:

```zig
const p: Point = .{ .x = 1, .y = 2 };
```

Tuple literal:

```zig
const args = .{ 1, "hello", true };
```

Format arguments are commonly passed this way.

```zig
std.debug.print("{d} {s}\n", .{ 10, "ok" });
```

### A.28 Operators

Arithmetic:

```zig
+  -  *  /  %
```

Assignment:

```zig
=  +=  -=  *=  /=  %=
```

Comparison:

```zig
==  !=  <  <=  >  >=
```

Boolean:

```zig
and  or  !
```

Bitwise:

```zig
&  |  ^  ~  <<  >>
```

Pointer and field:

```zig
&x
p.*
x.y
```

Optional and error:

```zig
x orelse y
try f()
f() catch y
```

### A.29 Builtin Functions

Builtin functions begin with `@`.

```zig
@import("std")
@This()
@TypeOf(x)
@sizeOf(T)
@alignOf(T)
@intCast(x)
@as(T, x)
@ptrCast(p)
@alignCast(p)
@panic("message")
```

A builtin is part of the language, not a normal library function.

### A.30 Tests

```zig
const std = @import("std");

test "addition" {
    try std.testing.expect(1 + 1 == 2);
}
```

Run tests:

```sh
zig test file.zig
```

A test block may use any code allowed in a function body.

### A.31 `unreachable`

```zig
switch (x) {
    0 => zero(),
    1 => one(),
    else => unreachable,
}
```

`unreachable` states that control cannot reach that point. Reaching it is a bug.

### A.32 `undefined`

```zig
var x: i32 = undefined;
```

`undefined` gives a value no defined contents. It is used when the program will write the value before reading it.

```zig
var buf: [1024]u8 = undefined;
```

Reading undefined memory is a bug.

### A.33 `pub`, `extern`, `export`, `inline`, `noinline`

```zig
pub fn f() void {}
```

```zig
extern fn puts(s: [*:0]const u8) c_int;
```

```zig
export fn add(a: i32, b: i32) i32 {
    return a + b;
}
```

```zig
inline fn small() void {}
noinline fn large() void {}
```

### A.34 Container Declarations

A container may be a file, struct, enum, union, or opaque type.

```zig
const S = struct {
    const Self = @This();

    value: i32,

    fn get(self: Self) i32 {
        return self.value;
    }
};
```

Declarations inside a container are accessed with dot syntax.

```zig
S.get
```

### A.35 Opaque Types

```zig
const Handle = opaque {};
```

An opaque type has unknown layout. It is useful for handles from C or private implementation details.

### A.36 Packed and Extern Layout

Packed struct:

```zig
const Flags = packed struct {
    a: bool,
    b: bool,
    c: u6,
};
```

Extern struct:

```zig
const CPoint = extern struct {
    x: c_int,
    y: c_int,
};
```

Use `extern` for C ABI layout. Use `packed` for bit-level layout.

### A.37 Common Program Shape

```zig
const std = @import("std");

pub fn main() !void {
    const allocator = std.heap.page_allocator;

    var list = std.ArrayList(u8).init(allocator);
    defer list.deinit();

    try list.append('A');

    std.debug.print("{c}\n", .{list.items[0]});
}
```

This shows the usual parts: import, entry point, allocator, cleanup, error propagation, and printing.

