Skip to content

Primitive Types

Every value in a Zig program has a type.

Every value in a Zig program has a type.

A type tells the compiler what kind of value something is and what operations are allowed on it.

For example:

const age = 30;

The value 30 is an integer.

const price = 19.99;

The value 19.99 is a floating-point number.

const enabled = true;

The value true is a boolean.

These basic built-in types are called primitive types.

Primitive types are the foundation of the language. More complex types such as arrays, structs, slices, and unions are built from them.

Integer Types

Integers are whole numbers.

Examples:

0
1
42
-10
1000000

Zig has many integer types because low-level programming often requires exact control over size and memory layout.

Common integer types:

TypeMeaningSize
i8signed integer8 bits
u8unsigned integer8 bits
i16signed integer16 bits
u16unsigned integer16 bits
i32signed integer32 bits
u32unsigned integer32 bits
i64signed integer64 bits
u64unsigned integer64 bits
isizesigned pointer-sized integerplatform dependent
usizeunsigned pointer-sized integerplatform dependent

The letter meanings are:

PrefixMeaning
isigned integer
uunsigned integer

Signed integers can store negative values:

const temperature: i32 = -10;

Unsigned integers cannot:

const count: u32 = 100;

This is invalid:

const value: u32 = -5; // error

Integer Ranges

The number of bits controls the range of values.

An 8-bit unsigned integer:

u8

can store:

0 to 255

A signed 8-bit integer:

i8

can store:

-128 to 127

Larger integer types can store larger numbers.

Why Exact Sizes Matter

In many high-level languages, programmers rarely think about memory size.

In Zig, exact sizes are important because Zig is designed for systems programming.

Examples:

  • network protocols
  • binary file formats
  • operating systems
  • embedded devices
  • graphics programming

Suppose a file format says:

this field is exactly 4 bytes

Then u32 is appropriate.

If a protocol says:

this value uses 1 byte

Then u8 is appropriate.

Zig makes these details explicit.

Floating-Point Types

Floating-point numbers store decimal values.

Examples:

const pi = 3.14159;
const temperature = -2.5;

Common floating-point types:

TypeSize
f1616 bits
f3232 bits
f6464 bits
f8080 bits
f128128 bits

The most common are:

f32
f64

f64 is commonly used because it gives better precision.

Example:

const price: f64 = 19.99;

Floating-Point Precision

Floating-point numbers are approximations.

This surprises many beginners.

For example:

const x: f64 = 0.1 + 0.2;

You might expect exactly:

0.3

But internally, floating-point values are stored in binary approximations.

This is normal in nearly all programming languages.

Because of this, direct equality checks with floats should be used carefully.

Boolean Type

A boolean stores either:

true
false

The type is:

bool

Example:

const is_admin: bool = true;
const finished: bool = false;

Booleans are commonly used with conditions:

if (is_admin) {
    // allowed
}

Character Data

Zig does not have a separate character type like some languages.

Instead, character values are usually represented with integers.

Example:

const letter: u8 = 'A';

The character 'A' is stored as its ASCII numeric value.

65

You can print it as a character:

const std = @import("std");

pub fn main() void {
    const letter: u8 = 'A';

    std.debug.print("{c}\n", .{letter});
}

Output:

A

The {c} formatter means:

print as character

Void Type

The type void means “no value.”

Example:

pub fn main() void {

}

The main function returns nothing.

A function returning void performs actions rather than producing a value.

The noreturn Type

Some code never returns.

Example:

@panic("fatal error");

A panic stops the program completely.

Functions that never return use the type:

noreturn

This helps the compiler reason about control flow.

The anyopaque Type

Zig sometimes needs to work with raw unknown data.

The type:

anyopaque

represents opaque memory whose structure is unknown.

This is common in C interoperability and low-level APIs.

Beginners usually do not need this immediately, but you will see it later in systems code.

Type Annotation

You can explicitly write a type:

const age: u8 = 30;

Syntax:

name: Type

Examples:

const enabled: bool = true;
const score: i32 = 100;
const pi: f64 = 3.14;

Type Inference

Often Zig can infer the type automatically.

const score = 100;
const pi = 3.14;

This is convenient for local values.

But explicit types are still useful when:

  • exact size matters
  • APIs require a specific type
  • clarity is important
  • interfacing with hardware or C

Printing Different Primitive Types

const std = @import("std");

pub fn main() void {
    const age: u8 = 25;
    const price: f64 = 19.99;
    const active: bool = true;
    const letter: u8 = 'Z';

    std.debug.print("Age: {}\n", .{age});
    std.debug.print("Price: {}\n", .{price});
    std.debug.print("Active: {}\n", .{active});
    std.debug.print("Letter: {c}\n", .{letter});
}

Output:

Age: 25
Price: 19.99
Active: true
Letter: Z

Choosing the Right Type

Good Zig programmers choose types carefully.

Examples:

SituationGood Type
byte datau8
file sizeusize
negative temperaturesi32
decimal pricesf64
true/false statebool

Choosing correct types improves:

  • memory efficiency
  • correctness
  • API clarity
  • compiler checks
  • portability

Primitive Types Are the Foundation

Almost everything in Zig eventually builds on primitive types.

Arrays contain primitive values.

Struct fields often use primitive types.

Pointers point to primitive data.

Binary formats are built from primitive types.

Memory layouts depend on primitive types.

If you understand primitive types clearly, the rest of the language becomes much easier to understand.