Intermediate

Hello World in Zig

Your first Zig program - the classic Hello World example with Docker setup

Every programming journey starts with Hello World. Let’s write our first Zig program.

The Code

Create a file named hello.zig:

1
2
3
4
5
6
const std = @import("std");

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();
    try stdout.print("Hello, World!\n", .{});
}

Understanding the Code

  • const std = @import("std") - Imports Zig’s standard library
  • @import - A built-in function (prefixed with @) for importing modules
  • pub fn main() !void - Declares the entry point; !void means it can return an error or void
  • std.io.getStdOut().writer() - Gets a writer for standard output
  • try - Propagates any error from the write operation
  • stdout.print - Writes formatted output to stdout
  • "Hello, World!\n" - The format string with a newline
  • .{} - An empty anonymous struct (required for formatting arguments)

Running with Docker

The easiest way to run this without installing Zig locally:

1
2
3
4
5
# Pull a Zig Docker image
docker pull euantorano/zig:master

# Run the program
docker run --rm -v $(pwd):/app -w /app euantorano/zig:master run hello.zig

Running Locally

If you have Zig installed:

1
2
3
4
5
6
# Run directly (compiles and executes)
zig run hello.zig

# Or build a binary first
zig build-exe hello.zig
./hello

Expected Output

Hello, World!

Key Concepts

  1. Standard library import - Nearly all Zig programs start with @import("std")
  2. Built-in functions - Prefixed with @, like @import, @intCast, etc.
  3. pub keyword - Makes functions and variables visible outside the module
  4. Format strings - Similar to printf but with compile-time type checking
  5. Anonymous structs - .{} syntax is used for passing arguments

Alternative: Using Debug Print

For quick debugging, you can use std.debug.print, which writes to stderr:

1
2
3
4
5
const std = @import("std");

pub fn main() void {
    std.debug.print("Hello, World!\n", .{});
}

Key differences:

  • void - No error handling needed (debug print doesn’t return errors)
  • No try - Debug print handles errors internally
  • Writes to stderr - Useful for debugging but not for normal program output

Understanding Error Handling

Zig has explicit error handling. The !void return type means:

1
2
3
4
pub fn main() !void {
    // Can return an error OR void
    // The '!' indicates this function may fail
}

This is fundamental to Zig’s philosophy of making everything explicit.

Compilation Options

Zig offers several build modes:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Default debug build (fast compilation, runtime checks)
zig run hello.zig

# Release build (optimized, no debug info)
zig run -O ReleaseFast hello.zig

# Small release (optimized for size)
zig run -O ReleaseSmall hello.zig

# Safe release (optimized with safety checks)
zig run -O ReleaseSafe hello.zig

Cross-Compilation

One of Zig’s killer features is easy cross-compilation:

1
2
3
4
5
6
7
8
# Build for Linux x86_64 from any platform
zig build-exe -target x86_64-linux hello.zig

# Build for Windows
zig build-exe -target x86_64-windows hello.zig

# Build for macOS ARM
zig build-exe -target aarch64-macos hello.zig

More Verbose Version

Here’s a version that shows more Zig concepts:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const std = @import("std");

const greeting: []const u8 = "Hello, World!";

pub fn main() void {
    // String concatenation at comptime
    const message = greeting ++ "\n";

    // Print the message
    std.debug.print("{s}", .{message});
}

This demonstrates:

  • []const u8 - Zig’s string type (a slice of constant bytes)
  • comptime - Operations performed at compile time
  • ++ - Array concatenation operator
  • {s} - Format specifier for string slices

Why Zig for Hello World?

Even this simple program demonstrates Zig’s design philosophy:

  • Explicit imports - No implicit global namespace
  • Clear entry point - pub fn main() with visible return type
  • No hidden magic - Every operation is visible in the code
  • Error awareness - Even basic I/O considers error handling

Next Steps

Continue to Variables and Data Types to explore Zig’s type system, including integers of any bit width, optionals, and comptime type introspection.

Running Today

All examples can be run using Docker:

docker pull euantorano/zig:master
Last updated: