A vector in Zig is a set of values of the same type designed for SIMD operations. When we talk about “vectors” in other languages, we usually think of dynamic lists, but in Zig, @Vector means something else.
In Zig, when we say vector, we refer to a fixed-length type on which we apply the same operation element by element.
Modern processors (x64 with AVX, ARM with NEON) can perform the same mathematical operation on multiple numbers simultaneously. This is known as SIMD (Single Instruction, Multiple Data).
Traditionally, programming this was a nightmare of unreadable and non-portable functions. Zig changes the game: it makes SIMD code native, clean, and portable.
What is @Vector?
A @Vector(length, type) in Zig is a fixed-size set of primitive elements (integers, floats, or pointers) that are processed in parallel.
Unlike an Array ([N]T), which is used to store data and access it individually, a Vector is designed to operate on all data at once.
Declaration and Usage
The syntax is simple. We use the built-in function @Vector.
const std = @import("std");
pub fn main() void {
// Define a vector of 4 floats (f32)
const a = @Vector(4, f32){ 1.0, 2.0, 3.0, 4.0 };
const b = @Vector(4, f32){ 5.0, 6.0, 7.0, 8.0 };
// Direct mathematical operation
const c = a + b;
// c is now { 6.0, 8.0, 10.0, 12.0 }
std.debug.print("Result: {any}\n", .{c});
}Notice a + b: we didn’t write a for loop, but the operation is still element-wise. The compiler can generate one or more SIMD instructions depending on the vector length and the target architecture.
Vector Operations
Almost all arithmetic and logical operators work with vectors:
- Arithmetic:
+,-,*,/(add/multiply element-wise). - Logical:
&,|,^,~. - Comparison:
==,<,>. (Caution! This returns a vector of booleans).
const v1 = @Vector(4, i32){ 10, 20, 30, 40 };
const v2 = @Vector(4, i32){ 10, 50, 30, 5 };
// Compare element-wise
const mask: @Vector(4, bool) = (v1 == v2);
// mask is { true, false, true, false }Broadcasting with @splat
To multiply all elements by a scalar, we first repeat that value in a vector using @splat.
const v = @Vector(4, f32){ 1.1, 2.2, 3.3, 4.4 };
const scale = 2.0;
// The scalar is "expanded" into a vector {2.0, 2.0, 2.0, 2.0}
const result = v * @as(@Vector(4, f32), @splat(scale));The return type of @splat is inferred from context. That’s why @as(@Vector(4, f32), @splat(scale)) indicates both the element type and the result length.
Automatic Portability
Operations shorter than the native SIMD width usually compile to a single instruction. Longer ones may need several, and if an operation lacks SIMD support on the target, the compiler operates element-wise.
If you write this code and compile for your PC (x86_64), Zig will use SSE or AVX registers.
If you compile for a Raspberry Pi (aarch64), Zig will use NEON instructions.
If you compile for an old microcontroller without SIMD, Zig will automatically generate a regular loop (polyfill).
The code does not fix AVX, NEON, or any specific family. The selection depends on the target and CPU features specified during compilation.
Arrays and Vectors: When to Use Each
It’s easy to get confused because both seem like “lists of things”.
| Feature | Array [N]T / Slice []T | Vector @Vector(N, T) |
|---|---|---|
| Purpose | Store and organize data. | Perform mathematical calculations. |
| Access | Individual (arr[i]) is fast. | Individual access can be slow. |
| Memory | Linearly structured. | Loaded into wide CPU registers. |
| Typical Use | Strings, buffers, inventories. | Physics, audio, graphics, cryptography. |
Don’t use @Vector for everything. Use it when you have computation-intensive loops (like processing an image, mixing audio, or multiplying matrices).
Conversion Between Arrays and Vectors
Often you’ll have your data in an array (read from a file) and want to process it quickly. You need to load it into a vector.
const array = [_]f32{ 1.0, 2.0, 3.0, 4.0 };
// Cast array to vector (if sizes match and are known at compile time)
const v: @Vector(4, f32) = array;
// ... operate on v ...
// Back to array
const result_array: [4]f32 = v;Practical Example: Dot Product
The dot product is a fundamental operation in 3D graphics and games. You multiply components and sum the result.
pub fn dotProduct(a: @Vector(4, f32), b: @Vector(4, f32)) f32 {
const mult = a * b; // { ax*bx, ay*by, az*bz, aw*bw }
// @reduce applies an operation to all elements of the vector
return @reduce(.Add, mult);
}The @reduce function applies a sequential horizontal reduction and converts the vector into a single value.