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
1000000Zig has many integer types because low-level programming often requires exact control over size and memory layout.
Common integer types:
| Type | Meaning | Size |
|---|---|---|
i8 | signed integer | 8 bits |
u8 | unsigned integer | 8 bits |
i16 | signed integer | 16 bits |
u16 | unsigned integer | 16 bits |
i32 | signed integer | 32 bits |
u32 | unsigned integer | 32 bits |
i64 | signed integer | 64 bits |
u64 | unsigned integer | 64 bits |
isize | signed pointer-sized integer | platform dependent |
usize | unsigned pointer-sized integer | platform dependent |
The letter meanings are:
| Prefix | Meaning |
|---|---|
i | signed integer |
u | unsigned 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; // errorInteger Ranges
The number of bits controls the range of values.
An 8-bit unsigned integer:
u8can store:
0 to 255A signed 8-bit integer:
i8can store:
-128 to 127Larger 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 bytesThen u32 is appropriate.
If a protocol says:
this value uses 1 byteThen 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:
| Type | Size |
|---|---|
f16 | 16 bits |
f32 | 32 bits |
f64 | 64 bits |
f80 | 80 bits |
f128 | 128 bits |
The most common are:
f32
f64f64 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.3But 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
falseThe type is:
boolExample:
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.
65You 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:
AThe {c} formatter means:
print as characterVoid 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:
noreturnThis helps the compiler reason about control flow.
The anyopaque Type
Zig sometimes needs to work with raw unknown data.
The type:
anyopaquerepresents 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: TypeExamples:
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: ZChoosing the Right Type
Good Zig programmers choose types carefully.
Examples:
| Situation | Good Type |
|---|---|
| byte data | u8 |
| file size | usize |
| negative temperatures | i32 |
| decimal prices | f64 |
| true/false state | bool |
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.