What Is a .ZIG File?
Zig source
Zig Programming Language (.zig)
Zig is a general-purpose programming language and toolchain designed to be a modern replacement for C. Created by Andrew Kelley in 2016, Zig emphasizes robustness, optimality, and maintainability while providing low-level control and high performance. The language aims to be simple, readable, and safe without sacrificing performance or control.
Technical Overview
Zig is a compiled, statically-typed language that provides manual memory management with safety features. It focuses on compile-time code execution, explicit control flow, and zero-cost abstractions. Zig can compile C and C++ code, making it an excellent choice for systems programming and as a C compiler replacement.
Key Features
- No Hidden Memory Allocations: All memory allocations are explicit
- Compile-time Code Execution: Rich compile-time programming capabilities
- Cross-compilation: First-class support for cross-compilation to many targets
- C Interoperability: Can import C headers and libraries directly
- Error Handling: Explicit error handling with error unions
- Memory Safety: Undefined behavior detection in debug builds
Zig Syntax and Structure
Basic Syntax
const std = @import("std");
const print = std.debug.print;
// Entry point
pub fn main() void {
print("Hello, Zig!\n");
}
// Variables
const message = "Hello"; // Compile-time constant
var counter: i32 = 0; // Mutable variable
// Types
const number: i32 = 42;
const pi: f64 = 3.14159;
const flag: bool = true;
const char: u8 = 'A';
// Arrays
const numbers = [_]i32{ 1, 2, 3, 4, 5 };
var buffer: [100]u8 = undefined;
// Slices
const slice = numbers[0..3]; // Slice of array
Functions
// Basic function
fn add(a: i32, b: i32) i32 {
return a + b;
}
// Function with error handling
fn divide(a: f64, b: f64) !f64 {
if (b == 0) {
return error.DivisionByZero;
}
return a / b;
}
// Generic function
fn max(comptime T: type, a: T, b: T) T {
return if (a > b) a else b;
}
// Function with multiple return values
fn divmod(a: i32, b: i32) struct { quotient: i32, remainder: i32 } {
return .{
.quotient = @divTrunc(a, b),
.remainder = @rem(a, b),
};
}
// Usage examples
pub fn main() void {
const result = add(5, 3);
print("5 + 3 = {}\n", .{result});
const division = divide(10, 2) catch |err| {
print("Error: {}\n", .{err});
return;
};
print("10 / 2 = {}\n", .{division});
const maximum = max(i32, 10, 20);
print("max(10, 20) = {}\n", .{maximum});
const dm = divmod(17, 5);
print("17 / 5 = {} remainder {}\n", .{ dm.quotient, dm.remainder });
}
Data Structures
// Structs
const Point = struct {
x: f64,
y: f64,
// Method
pub fn distance(self: Point, other: Point) f64 {
const dx = self.x - other.x;
const dy = self.y - other.y;
return @sqrt(dx * dx + dy * dy);
}
// Associated function
pub fn origin() Point {
return Point{ .x = 0, .y = 0 };
}
};
// Enums
const Color = enum {
red,
green,
blue,
pub fn rgb(self: Color) [3]u8 {
return switch (self) {
.red => [_]u8{ 255, 0, 0 },
.green => [_]u8{ 0, 255, 0 },
.blue => [_]u8{ 0, 0, 255 },
};
}
};
// Tagged unions
const Value = union(enum) {
integer: i32,
float: f64,
string: []const u8,
pub fn print(self: Value) void {
switch (self) {
.integer => |i| std.debug.print("Integer: {}\n", .{i}),
.float => |f| std.debug.print("Float: {}\n", .{f}),
.string => |s| std.debug.print("String: {s}\n", .{s}),
}
}
};
Error Handling
Error Types and Handling
// Define error types
const FileError = error{
FileNotFound,
PermissionDenied,
OutOfMemory,
};
const NetworkError = error{
ConnectionRefused,
Timeout,
InvalidAddress,
};
// Error union
fn readFile(path: []const u8) FileError![]u8 {
// Implementation would go here
if (std.mem.eql(u8, path, "nonexistent.txt")) {
return FileError.FileNotFound;
}
return "file contents";
}
// Error handling patterns
pub fn main() !void {
// Try expression
const content = readFile("config.txt") catch |err| switch (err) {
FileError.FileNotFound => "default config",
FileError.PermissionDenied => return err,
FileError.OutOfMemory => return err,
};
// If expression with error capture
if (readFile("data.txt")) |data| {
print("File content: {s}\n", .{data});
} else |err| {
print("Error reading file: {}\n", .{err});
}
// Propagate errors with try
const important_data = try readFile("important.txt");
print("Important data: {s}\n", .{important_data});
}
Memory Management
Allocators
const std = @import("std");
const ArrayList = std.ArrayList;
pub fn main() !void {
// General purpose allocator
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Dynamic array
var list = ArrayList(i32).init(allocator);
defer list.deinit();
try list.append(1);
try list.append(2);
try list.append(3);
for (list.items) |item| {
print("{} ", .{item});
}
print("\n");
// Manual memory management
const buffer = try allocator.alloc(u8, 1024);
defer allocator.free(buffer);
// Arena allocator for temporary allocations
var arena = std.heap.ArenaAllocator.init(allocator);
defer arena.deinit();
const arena_allocator = arena.allocator();
const temp_data = try arena_allocator.alloc(i32, 100);
// No need to free temp_data, arena handles it
}
Custom Allocators
const FixedBufferAllocator = struct {
buffer: []u8,
offset: usize = 0,
const Self = @This();
pub fn allocator(self: *Self) std.mem.Allocator {
return std.mem.Allocator.init(self, alloc, resize, free);
}
fn alloc(self: *Self, len: usize, alignment: u29, len_align: u29, ra: usize) ?[*]u8 {
_ = len_align;
_ = ra;
const adjusted_len = std.mem.alignForward(len, alignment);
if (self.offset + adjusted_len > self.buffer.len) {
return null;
}
const result = self.buffer.ptr + self.offset;
self.offset += adjusted_len;
return result;
}
fn resize(self: *Self, buf: []u8, buf_align: u29, new_len: usize, len_align: u29, ra: usize) ?usize {
_ = self;
_ = buf;
_ = buf_align;
_ = len_align;
_ = ra;
return new_len;
}
fn free(self: *Self, buf: []u8, buf_align: u29, ra: usize) void {
_ = self;
_ = buf;
_ = buf_align;
_ = ra;
// Fixed buffer allocator doesn't free individual allocations
}
};
Compile-time Programming
Comptime Features
// Compile-time function execution
fn fibonacci(n: u32) u32 {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
const fib_10 = fibonacci(10); // Computed at compile time
// Generic programming
fn Vec(comptime T: type, comptime size: usize) type {
return struct {
data: [size]T,
const Self = @This();
pub fn init() Self {
return Self{ .data = [_]T{0} ** size };
}
pub fn get(self: Self, index: usize) T {
return self.data[index];
}
pub fn set(self: *Self, index: usize, value: T) void {
self.data[index] = value;
}
pub fn dot(self: Self, other: Self) T {
var result: T = 0;
for (self.data, other.data) |a, b| {
result += a * b;
}
return result;
}
};
}
// Usage
const Vec3f = Vec(f32, 3);
const Vec4i = Vec(i32, 4);
pub fn main() void {
var v1 = Vec3f.init();
var v2 = Vec3f.init();
v1.set(0, 1.0);
v1.set(1, 2.0);
v1.set(2, 3.0);
v2.set(0, 4.0);
v2.set(1, 5.0);
v2.set(2, 6.0);
const dot_product = v1.dot(v2);
print("Dot product: {}\n", .{dot_product});
}
C Interoperability
Calling C Functions
// Import C library
const c = @cImport({
@cInclude("stdio.h");
@cInclude("stdlib.h");
@cInclude("math.h");
});
pub fn main() void {
// Call C functions
_ = c.printf("Hello from C!\n");
const x: f64 = 2.0;
const result = c.pow(x, 3.0);
_ = c.printf("2^3 = %f\n", result);
// Allocate memory using C malloc
const ptr = c.malloc(100);
defer c.free(ptr);
if (ptr) |valid_ptr| {
const buffer: [*]u8 = @ptrCast(valid_ptr);
buffer[0] = 65; // 'A'
_ = c.printf("First byte: %c\n", buffer[0]);
}
}
Exposing Zig Functions to C
// Export Zig function for C
export fn add_numbers(a: c_int, b: c_int) c_int {
return a + b;
}
export fn process_array(arr: [*]c_int, len: c_size_t) c_int {
var sum: c_int = 0;
var i: c_size_t = 0;
while (i < len) : (i += 1) {
sum += arr[i];
}
return sum;
}
// C header would declare:
// int add_numbers(int a, int b);
// int process_array(int* arr, size_t len);
Build System and Tooling
build.zig
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
// Executable
const exe = b.addExecutable(.{
.name = "myapp",
.root_source_file = .{ .path = "src/main.zig" },
.target = target,
.optimize = optimize,
});
// Link C library
exe.linkLibC();
exe.linkSystemLibrary("sqlite3");
// Add include path
exe.addIncludePath(.{ .path = "include" });
// Install artifact
b.installArtifact(exe);
// Run command
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| {
run_cmd.addArgs(args);
}
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
// Tests
const unit_tests = b.addTest(.{
.root_source_file = .{ .path = "src/main.zig" },
.target = target,
.optimize = optimize,
});
const run_unit_tests = b.addRunArtifact(unit_tests);
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&run_unit_tests.step);
}
Common Commands
# Initialize new project
zig init-exe
zig init-lib
# Build project
zig build
# Run project
zig build run
# Run tests
zig build test
# Cross-compile
zig build -Dtarget=x86_64-windows
zig build -Dtarget=aarch64-linux
# Format code
zig fmt src/
Testing
Unit Tests
const std = @import("std");
const testing = std.testing;
fn add(a: i32, b: i32) i32 {
return a + b;
}
fn divide(a: f64, b: f64) !f64 {
if (b == 0) return error.DivisionByZero;
return a / b;
}
test "addition" {
try testing.expect(add(2, 3) == 5);
try testing.expect(add(-1, 1) == 0);
}
test "division" {
try testing.expectEqual(@as(f64, 2.5), try divide(5, 2));
try testing.expectError(error.DivisionByZero, divide(5, 0));
}
test "memory allocation" {
const allocator = testing.allocator;
const buffer = try allocator.alloc(u8, 100);
defer allocator.free(buffer);
try testing.expect(buffer.len == 100);
buffer[0] = 42;
try testing.expect(buffer[0] == 42);
}
File Format Details
- MIME Type:
text/x-zig - File Extension:
.zig - Character Encoding: UTF-8
- Line Endings: LF (Unix-style) preferred
- Indentation: 4 spaces (by convention)
Zig represents a modern approach to systems programming, offering the performance and control of C with improved safety, better tooling, and more expressive language features. Its focus on simplicity and explicitness makes it an attractive choice for developers who need low-level control without sacrificing code clarity and maintainability.
File Information
Zig source
Code
.zig
text/x-zig
Related File Types
Other file types in the Code category you might also need:
Start Analyzing ZIG Files Now
Use our free AI-powered tool to detect and analyze Zig source files instantly with Google's Magika technology.
⚡Try File Detection Tool