# Undefined Values

### Undefined Values

In Zig, `undefined` means “this value has not been initialized.”

It is not zero.

It is not `null`.

It is not an empty value.

It means the memory exists, but its contents are not meaningful yet.

```zig
var x: i32 = undefined;
```

This creates a variable named `x` with type `i32`, but it does not give `x` a real value.

#### Why `undefined` Exists

Sometimes you need to reserve space first and fill it later.

For example:

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

This creates an array of 1024 bytes. Zig does not spend time filling the array with zeros. That can be useful for performance.

Later, you should write real data into it:

```zig
buffer[0] = 65;
buffer[1] = 66;
buffer[2] = 67;
```

Now the first three bytes have meaningful values.

#### Reading Undefined Values Is a Bug

This is wrong:

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

pub fn main() void {
    var x: i32 = undefined;

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

The variable `x` has not been initialized. Reading it is invalid program behavior.

The value might look random. It might appear to work once. It might fail later. You cannot rely on it.

`undefined` is a promise from you to the compiler: “I will write a valid value before reading this.”

If you break that promise, the program is wrong.

#### Prefer Normal Initialization

For beginner code, prefer this:

```zig
const x: i32 = 42;
```

or:

```zig
var count: usize = 0;
```

Instead of this:

```zig
var count: usize = undefined;
count = 0;
```

The first version is clearer and safer.

Use `undefined` only when you have a real reason.

#### Common Use: Buffers

A common valid use is temporary storage.

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

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

    const message = "hello";

    @memcpy(buffer[0..message.len], message);

    std.debug.print("{s}\n", .{buffer[0..message.len]});
}
```

Here, the full buffer starts as undefined. That is acceptable because we only read the part we filled.

This line writes into the first part of the buffer:

```zig
@memcpy(buffer[0..message.len], message);
```

This line reads only the initialized part:

```zig
buffer[0..message.len]
```

The rest of the buffer is still undefined, but we do not read it.

#### Common Use: Output Parameters

Some APIs fill memory that you provide.

A simplified example:

```zig
fn fillNumber(out: *i32) void {
    out.* = 123;
}
```

The caller can create storage first:

```zig
var value: i32 = undefined;
fillNumber(&value);
```

After the function call, `value` has been initialized.

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

fn fillNumber(out: *i32) void {
    out.* = 123;
}

pub fn main() void {
    var value: i32 = undefined;

    fillNumber(&value);

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

Output:

```text
123
```

This pattern is useful when a function needs to write into caller-owned memory.

#### `undefined` with Arrays

Arrays are often initialized with `undefined` when they will be filled later.

```zig
var numbers: [3]i32 = undefined;

numbers[0] = 10;
numbers[1] = 20;
numbers[2] = 30;
```

Now all elements have real values.

This is safe:

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

pub fn main() void {
    var numbers: [3]i32 = undefined;

    numbers[0] = 10;
    numbers[1] = 20;
    numbers[2] = 30;

    std.debug.print("{} {} {}\n", .{
        numbers[0],
        numbers[1],
        numbers[2],
    });
}
```

This is not safe:

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

pub fn main() void {
    var numbers: [3]i32 = undefined;

    numbers[0] = 10;

    std.debug.print("{}\n", .{numbers[1]}); // bug
}
```

Only `numbers[0]` was initialized. Reading `numbers[1]` is wrong.

#### `undefined` with Structs

You can also use `undefined` with structs.

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

var p: Point = undefined;
```

But you must initialize fields before reading them.

```zig
p.x = 10;
p.y = 20;
```

Complete example:

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

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

pub fn main() void {
    var p: Point = undefined;

    p.x = 10;
    p.y = 20;

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

Output:

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

This is wrong:

```zig
var p: Point = undefined;

p.x = 10;

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

The field `p.y` has not been initialized.

#### Partial Initialization Requires Discipline

`undefined` lets you initialize data step by step.

That power is useful, but it requires care.

This is fine:

```zig
var pair: [2]u8 = undefined;
pair[0] = 1;
pair[1] = 2;
```

This is dangerous:

```zig
var pair: [2]u8 = undefined;
pair[0] = 1;

// pair[1] is still undefined
```

The compiler cannot always prove whether every part has been initialized before use. You are responsible for using `undefined` correctly.

#### `undefined` Is Different from Zero

This is initialized:

```zig
var x: i32 = 0;
```

This is not:

```zig
var x: i32 = undefined;
```

Zero is a real value. Undefined is not.

This array is filled with zero bytes:

```zig
var buffer = [_]u8{0} ** 64;
```

This array is not filled with meaningful bytes:

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

Use zero initialization when you need zeros.

Use `undefined` when you will overwrite the data before reading it.

#### `undefined` Is Different from `null`

`null` means “no value” for optional types.

```zig
var maybe_number: ?i32 = null;
```

This variable has a meaningful value: it contains no integer right now.

`undefined` means there is no meaningful value at all.

```zig
var maybe_number: ?i32 = undefined;
```

This does not mean `null`. It means the optional itself has not been initialized.

That distinction matters.

Prefer this when you mean “empty”:

```zig
var maybe_number: ?i32 = null;
```

Use this only when you will assign a real optional value before reading it:

```zig
var maybe_number: ?i32 = undefined;
```

#### Debug Builds May Help, But Do Not Rely on It

In debug-oriented builds, Zig may fill undefined memory with recognizable patterns to help catch bugs. That can make errors easier to notice.

But you should not depend on any particular value.

This is still wrong:

```zig
var x: i32 = undefined;
std.debug.print("{}\n", .{x});
```

The rule is simple: never read `undefined`.

#### A Complete Example: Filling a Buffer

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

fn writeGreeting(buffer: []u8) []u8 {
    const text = "hello";

    @memcpy(buffer[0..text.len], text);

    return buffer[0..text.len];
}

pub fn main() void {
    var storage: [64]u8 = undefined;

    const greeting = writeGreeting(&storage);

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

Output:

```text
hello
```

The storage starts undefined:

```zig
var storage: [64]u8 = undefined;
```

The function writes into part of it:

```zig
@memcpy(buffer[0..text.len], text);
```

The program reads only the initialized part:

```zig
const greeting = writeGreeting(&storage);
```

That is the correct pattern.

#### When to Use `undefined`

Use `undefined` when:

| Situation | Reason |
|---|---|
| A buffer will be filled immediately | avoids unnecessary initialization |
| An API writes into caller-provided memory | storage exists before value is written |
| You are building a value step by step | fields or elements are assigned later |
| Performance matters and initialization would be wasted | avoids extra work |

Avoid `undefined` when:

| Situation | Better choice |
|---|---|
| You already know the value | initialize directly |
| You mean zero | use `0` or zero-filled data |
| You mean no value | use `null` with an optional |
| You are unsure | do not use `undefined` |

#### The Main Idea

`undefined` is not a value you use. It is a way to create storage without initializing it.

It can make code faster and more flexible, especially for buffers and low-level APIs. But it also removes a safety net.

The discipline is simple: write before read.

