# Formatting and Printing

### Formatting and Printing

Formatting means turning values into text.

Printing means sending that text somewhere, usually to the terminal.

You have already used this many times:

```zig
const std = @import("std");

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

This line is doing two things:

```zig
std.debug.print("Hello, Zig!\n", .{});
```

It prints a format string.

It receives a tuple of values to insert into that string.

The tuple is empty here:

```zig
.{}
```

because there are no values to insert.

#### Placeholders

A placeholder marks where a value should go.

```zig
std.debug.print("x = {}\n", .{42});
```

Output:

```text
x = 42
```

The `{}` means “format this value using its default format.”

The value comes from the tuple:

```zig
.{42}
```

If there are two placeholders, you need two values:

```zig
std.debug.print("{} + {} = {}\n", .{ 2, 3, 5 });
```

Output:

```text
2 + 3 = 5
```

The order matters. The first `{}` gets `2`, the second gets `3`, the third gets `5`.

#### Printing Strings

For strings, use `{s}`.

```zig
const std = @import("std");

pub fn main() void {
    const name = "Zig";
    std.debug.print("Hello, {s}!\n", .{name});
}
```

Output:

```text
Hello, Zig!
```

A string literal such as `"Zig"` is a sequence of bytes. The `{s}` formatter says to print those bytes as a string.

If you use `{}` for a byte slice, Zig may print it in a less friendly form. For human-readable text, use `{s}`.

#### Printing Characters

A byte character can be printed with `{c}`.

```zig
const std = @import("std");

pub fn main() void {
    const letter: u8 = 'A';
    std.debug.print("letter = {c}\n", .{letter});
}
```

Output:

```text
letter = A
```

This works for byte-sized characters. For full Unicode text, remember that strings are UTF-8 bytes, and one visible character may use more than one byte.

#### Printing Integers

The default integer format is decimal:

```zig
std.debug.print("{}\n", .{255});
```

Output:

```text
255
```

You can also print in hexadecimal:

```zig
std.debug.print("{x}\n", .{255});
```

Output:

```text
ff
```

Uppercase hexadecimal:

```zig
std.debug.print("{X}\n", .{255});
```

Output:

```text
FF
```

Binary:

```zig
std.debug.print("{b}\n", .{10});
```

Output:

```text
1010
```

This is useful when working with flags, bytes, encodings, file formats, networking, and low-level code.

#### Printing Floating Point Values

Floating point values can be printed with the default formatter:

```zig
const pi: f64 = 3.1415926535;
std.debug.print("pi = {}\n", .{pi});
```

You can also choose precision:

```zig
std.debug.print("pi = {d:.2}\n", .{pi});
```

Output:

```text
pi = 3.14
```

The `.2` means two digits after the decimal point.

#### Printing Booleans

Booleans print naturally:

```zig
const ok = true;
std.debug.print("ok = {}\n", .{ok});
```

Output:

```text
ok = true
```

The value `false` prints as:

```text
false
```

#### Printing Types

Zig can print type names at compile time using `@typeName`.

```zig
const std = @import("std");

pub fn main() void {
    std.debug.print("type = {s}\n", .{@typeName(u32)});
}
```

Output:

```text
type = u32
```

This is useful when learning generics and `comptime`.

#### Format Strings Are Checked

Zig checks format strings.

If you write a placeholder but do not pass enough values, the compiler reports an error.

Wrong:

```zig
std.debug.print("{} {}\n", .{42});
```

There are two placeholders, but only one value.

Wrong:

```zig
std.debug.print("{s}\n", .{42});
```

`{s}` expects string-like data, but `42` is an integer.

This is one of Zig’s strengths. Formatting mistakes are usually caught early.

#### Formatting Into a Buffer

Printing sends text to an output destination.

Sometimes you want to create text and store it in memory.

Use `std.fmt.bufPrint`.

```zig
const std = @import("std");

pub fn main() !void {
    var buffer: [100]u8 = undefined;

    const text = try std.fmt.bufPrint(&buffer, "name={s}, age={}", .{
        "Alice",
        30,
    });

    std.debug.print("{s}\n", .{text});
}
```

Output:

```text
name=Alice, age=30
```

The buffer is fixed size:

```zig
var buffer: [100]u8 = undefined;
```

`bufPrint` writes into that buffer and returns the used slice:

```zig
const text = try std.fmt.bufPrint(&buffer, "name={s}, age={}", .{
    "Alice",
    30,
});
```

The returned `text` is not a new allocation. It is a slice pointing into `buffer`.

#### Buffer Size Matters

If the buffer is too small, `bufPrint` returns an error.

```zig
var buffer: [4]u8 = undefined;

const text = try std.fmt.bufPrint(&buffer, "hello", .{});
```

The word `"hello"` needs 5 bytes, but the buffer has only 4. The operation fails instead of writing past the end.

This is safer than C-style formatting functions that can easily overflow a buffer when used incorrectly.

#### Allocating Formatted Text

Sometimes you do not know the output size ahead of time.

In that case, use an allocator-based formatting function.

The exact helper names can vary across Zig versions, but the common idea is:

```zig
const text = try std.fmt.allocPrint(allocator, "id={}", .{123});
defer allocator.free(text);
```

This allocates enough memory for the formatted text.

Because it allocates, it can fail.

Because it allocates, you must free it.

The pattern is:

```zig
const text = try make_formatted_text;
defer allocator.free(text);
```

Use buffer formatting when a fixed maximum size is reasonable.

Use allocator formatting when the size is naturally dynamic.

#### Printing Custom Structs

Suppose you have this struct:

```zig
const Point = struct {
    x: i32,
    y: i32,
};
```

You can print its fields manually:

```zig
const std = @import("std");

const Point = struct {
    x: i32,
    y: i32,
};

pub fn main() void {
    const p = Point{ .x = 10, .y = 20 };

    std.debug.print("Point({}, {})\n", .{ p.x, p.y });
}
```

Output:

```text
Point(10, 20)
```

This is often the clearest beginner approach.

Later, you can define custom formatting behavior for your own types, but manual field printing is enough for now.

#### Debug Printing

The formatter `{any}` is useful for debugging many values.

```zig
const std = @import("std");

const Point = struct {
    x: i32,
    y: i32,
};

pub fn main() void {
    const p = Point{ .x = 10, .y = 20 };

    std.debug.print("{any}\n", .{p});
}
```

This can print a representation of the value useful during development.

For public output, write a deliberate format.

For quick inspection, `{any}` is convenient.

#### Printing Arrays and Slices

For text slices, use `{s}`:

```zig
const name: []const u8 = "Alice";
std.debug.print("{s}\n", .{name});
```

For non-text arrays or slices, use `{any}` while debugging:

```zig
const numbers = [_]u8{ 1, 2, 3 };
std.debug.print("{any}\n", .{numbers});
```

A byte slice can be either text or raw bytes. The formatter you choose tells Zig how you want to view it.

Text view:

```zig
std.debug.print("{s}\n", .{"abc"});
```

Debug view:

```zig
std.debug.print("{any}\n", .{"abc"});
```

#### Newlines

Printing does not automatically add a newline.

This:

```zig
std.debug.print("hello", .{});
std.debug.print("world", .{});
```

prints:

```text
helloworld
```

Add `\n` when you want a new line:

```zig
std.debug.print("hello\n", .{});
std.debug.print("world\n", .{});
```

Output:

```text
hello
world
```

The sequence `\n` means newline.

#### Escaping Special Characters

Some characters need escape sequences inside string literals.

Newline:

```zig
"\n"
```

Tab:

```zig
"\t"
```

Double quote:

```zig
"She said \"hello\""
```

Backslash:

```zig
"C:\\Users\\Alice"
```

Example:

```zig
std.debug.print("path = C:\\Users\\Alice\n", .{});
```

Output:

```text
path = C:\Users\Alice
```

#### Standard Error vs Standard Output

`std.debug.print` is mainly for debugging. It writes to standard error.

For serious command-line programs, you often distinguish between:

standard output for normal program output

standard error for diagnostics and errors

For example, a tool that prints JSON should write the JSON to stdout and error messages to stderr.

In early examples, `std.debug.print` is fine. It is simple and available. Later, when building real command-line tools, use explicit stdout and stderr writers.

#### Formatting Is Compile-Time Aware

Zig format strings are usually known at compile time.

That lets Zig check the format string against the arguments.

This fits the larger Zig style: catch mistakes before the program runs when possible.

Example:

```zig
std.debug.print("value = {}\n", .{123});
```

The compiler can inspect the format string and the argument tuple.

That is why Zig formatting feels stricter than formatting in many dynamic languages.

#### A Small Report Program

Here is a complete program that prints a small report:

```zig
const std = @import("std");

pub fn main() void {
    const name = "Alice";
    const score: u32 = 42;
    const passed = score >= 30;

    std.debug.print("Student report\n", .{});
    std.debug.print("name: {s}\n", .{name});
    std.debug.print("score: {}\n", .{score});
    std.debug.print("passed: {}\n", .{passed});
}
```

Output:

```text
Student report
name: Alice
score: 42
passed: true
```

This example uses the common formatters:

`{s}` for strings

`{}` for integers and booleans

`\n` for line breaks

#### A Small Buffer Formatting Program

This program creates a formatted message in memory before printing it:

```zig
const std = @import("std");

pub fn main() !void {
    var buffer: [128]u8 = undefined;

    const user_id: u32 = 1001;
    const active = true;

    const message = try std.fmt.bufPrint(
        &buffer,
        "user_id={}, active={}",
        .{ user_id, active },
    );

    std.debug.print("{s}\n", .{message});
}
```

Output:

```text
user_id=1001, active=true
```

This pattern is useful when you need formatted text for a file, a network message, a log line, or a larger string.

#### The Core Pattern

Most beginner formatting code follows this shape:

```zig
std.debug.print("text with {}\n", .{value});
```

For strings:

```zig
std.debug.print("hello {s}\n", .{name});
```

For buffer formatting:

```zig
var buffer: [128]u8 = undefined;
const text = try std.fmt.bufPrint(&buffer, "x = {}", .{x});
```

For allocated formatting:

```zig
const text = try std.fmt.allocPrint(allocator, "x = {}", .{x});
defer allocator.free(text);
```

#### What You Should Remember

Formatting turns values into text.

Printing sends text to an output destination.

`std.debug.print` is useful while learning.

The format string comes first.

The arguments come second as a tuple.

Use `{}` for default formatting.

Use `{s}` for strings.

Use `{c}` for characters.

Use `{x}`, `{X}`, and `{b}` for hexadecimal and binary integers.

Use `std.fmt.bufPrint` to format text into a fixed buffer.

Use allocator-based formatting when the size is dynamic.

Zig checks many formatting mistakes at compile time, which makes formatting safer than it first appears.

