Basic Data Types
You’ve probably already noticed, from the previous examples, that Zig uses data types in a very strict way. In some cases, Zig can automatically determine these types. This is technically called type inference.
|
const n_x = 1; |
Here, for example, Zig infers that n_x is an integer number, but we can also specify the type explicitly.
|
const n_x: u1 = 1; |
Type u1? Yes. The u comes from unsigned (an unsigned integer), and the 1 indicates the number of bits the value will use in memory. A bit is the smallest unit of data representation in memory. It can hold only two values: 0 or 1.
If we think of a bit as a box that can store either a 0 or a 1:
|
0 |
|
bit |
|
Fig. un bit |
It’s clear that with a single bit we can represent just a 0 or a 1 - nothing more, nothing less. Since we only have one bit, the smallest number we can represent is 0, and the largest is 1.
|
0 |
ó |
1 |
|
2⁰ |
2⁰ |
|
|
fig. one bit |
||
Since u1 uses just one bit, if we try to assign a number greater than 1 to a u1 data type, the following will happen:
const_2.zig |
|
const std = @import("std"); pub fn main() u8 { const n_a: u1 = 2; return n_a; } |
|
$ zig run const_2.zig |
|
const_2.zig:4:21: error: type 'u1' cannot represent integer value '2' const n_a: u1 = 2; ^ |
The compiler tells us that we can’t represent the value 2 with a one-bit integer.
So what can we do? How many bits do we need to represent the number 2?
There’s an old joke among programmers that goes:
There are 10 types of people - those who understand binary and those who don’t.
|
const n_x: u2 = 2; |
If we change the type to u2, that means we now have 2 bits for our data. That gives us four possible combinations:
|
0
|
1
|
2
|
3
|
||||||||||||||||||
|
fig. two bits |
With two bits, we can represent values from 0 up to:
2¹ + 2⁰ = 2 + 1 = 3
In other words, values between 0 and 3.
Do you need to know all the types in detail and by heart?
To begin with, it’s enough to get familiar with: u8, i32, usize, f32, bool, and void.
To give you a global picture and help you start exploring Zig’s primitive types, here’s a summary:
Booleans
The values a boolean type can take are only two: true (True) or false (False). These, along with the boolean operators we’ll see in the next chapter, make it possible to evaluate expressions and then make different decisions in our code.
|
bool |
True or false values |
|
Integer (whole) numbers
Zig supports integer types of any bit size, like u3, i5, or u37. This isn’t common in other languages and lets you fine-tune memory usage to the maximum.
|
u8 |
Unsigned 8-bit integer |
0 to 255 |
|
i8 |
Signed 8-bit integer |
-128 to 127 |
|
u16 |
Unsigned 16-bit integer |
0 to 65,535 |
|
i16 |
Signed 16-bit integer |
-32,768 to 32,767 |
|
u32 |
Unsigned 32-bit integer |
0 to ~4 billion |
|
i32 |
Signed 32-bit integer |
~ -2 billion to 2 billion |
|
usize |
Unsigned integer the size of the system (useful for counting things like bytes) |
|
Floating-point numbers
|
f16 |
Low Precision (16 bits) |
Very compact, rarely used |
|
f32 |
Medium Precision (32 bits) |
Enough for most cases |
|
f64 |
High Precision (64 bits) |
High numerical accuracy |
Other types
|
void |
No value (used for functions that return nothing) |
|
noreturn |
Something that never returns (like return, unreachable) |
|
comptime_int |
Special type used only at compile time |
|
anyerror |
Any kind of error, for error handling |
|
type |
The type of types (yes, it’s strange - but useful in Zig) |