⚡ Zig Guide LiveUnofficial
✓ Zig 0.17.0-dev.1454+5faa79730On an older Zig?

Integer Rules

const std = @import("std");
const expect = std.testing.expect;

test "arbitrary bit widths" {
    // Any width from u0/i0 up to u65535 is a real type.
    const small: u3 = 7;
    try expect(@as(u8, small) == 7);
    try expect(std.math.maxInt(u3) == 7);
}

test "widening is implicit, narrowing is not" {
    const a: u8 = 200;
    const b: u16 = a; // always safe, so allowed
    try expect(b == 200);

    // `const c: u8 = b;` would not compile. Say what you mean:
    const c: u8 = @intCast(b); // checked in safety builds
    try expect(c == 200);
}

test "wrapping and saturating operators" {
    const max: u8 = 255;
    // Plain `max + 1` is illegal behaviour (a panic in safety builds).
    try expect(max +% 1 == 0); // wrapping
    try expect(max +| 1 == 255); // saturating
}

test "overflow can be detected instead" {
    const max: u8 = 255;
    const result = @addWithOverflow(max, 1);
    try expect(result[0] == 0); // wrapped value
    try expect(result[1] == 1); // overflow bit set
}

test "comptime_int has no width" {
    // Literals are arbitrary precision until they are given a type.
    const big = 1 << 100;
    try expect(big > 0);
    try expect(@TypeOf(1 + 1) == comptime_int);
}

Any width you like

u3, i7, u64, u1000 are all real types. u8 is not special, it is just the one that happens to be a byte. Packed structs use this to describe bitfields exactly.

Widening is free, narrowing is not

u8u16 is always safe, so Zig does it implicitly. The reverse can lose data, so it must be written:

const c: u8 = @intCast(b);

In a safety-enabled build @intCast panics if the value does not fit. In ReleaseFast it is illegal behaviour. Either way, the cast is visible in the source, so you cannot lose the top bits by accident.

Overflow is a decision

Plain + on integers traps on overflow rather than wrapping. If you want different behaviour, ask for it:

OperatorOn overflow
+ - *illegal behaviour (panics in safety builds)
+% -% *%wrap around
+| -| *|saturate at the type’s limit

Or detect it: @addWithOverflow(a, b) returns a tuple of the wrapped result and an overflow bit.

This is C’s biggest silent-corruption source turned into an explicit choice.

comptime_int

Literals have no width until they are assigned one. 1 << 100 is fine at compile time; it only needs to fit once it becomes a runtime value.