# Returning a Struct

### Multiple Return Patterns

Many functions need to produce more than one piece of information.

For example:

- a parser may return both a value and a position
- a file loader may return data and metadata
- a math function may return multiple coordinates
- a search function may return a result and a status

Some languages support multiple return values directly.

Zig takes a simpler approach.

Instead of special syntax, Zig usually uses:

- structs
- error unions
- optionals
- pointers to output values

This keeps the language small and predictable.

## Returning a Struct

The most common pattern is returning a struct.

Example:

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

fn createPoint() Point {
    return Point{
        .x = 10.5,
        .y = 20.0,
    };
}
```

Calling:

```zig
const p = createPoint();
```

Using the result:

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

Output:

```text
(10.5, 20)
```

This is Zig’s preferred style.

A struct groups related data together clearly.

## Anonymous Struct Returns

Sometimes you only need a temporary structure.

Example:

```zig
fn getSize() struct { width: u32, height: u32 } {
    return .{
        .width = 1920,
        .height = 1080,
    };
}
```

Calling:

```zig
const size = getSize();
```

Using it:

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

Output:

```text
1920x1080
```

Anonymous structs are useful for small grouped results.

## Returning Success or Failure

One of the most important Zig patterns is returning either:

- a value
- or an error

Example:

```zig
fn divide(a: f64, b: f64) !f64 {
    if (b == 0) {
        return error.DivisionByZero;
    }

    return a / b;
}
```

The return type:

```zig
!f64
```

means:

- success → `f64`
- failure → error

Calling:

```zig
const result = try divide(10, 2);
```

This pattern is everywhere in Zig.

Unlike exceptions, errors are visible directly in the type system.

## Returning Optional Values

Sometimes a function may not find a result.

Instead of throwing an error, Zig often uses optionals.

Example:

```zig
fn findEven(value: i32) ?i32 {
    if (value % 2 == 0) {
        return value;
    }

    return null;
}
```

The type:

```zig
?i32
```

means:

- either an `i32`
- or `null`

Calling:

```zig
const result = findEven(10);
```

Checking:

```zig
if (result) |value| {
    std.debug.print("{}\n", .{value});
} else {
    std.debug.print("not found\n", .{});
}
```

Optionals are useful when “no result” is normal behavior.

## Combining Errors and Optionals

Functions may combine both ideas.

Example:

```zig
fn parseNumber(text: []const u8) !?i32 {

}
```

Meaning:

- the function may fail with an error
- the function may return `null`
- the function may return an integer

This can look complicated initially, but it becomes natural with practice.

## Output Parameters

Sometimes functions write results into caller-provided memory.

Example:

```zig
fn swap(a: *i32, b: *i32) void {
    const temp = a.*;
    a.* = b.*;
    b.* = temp;
}
```

Calling:

```zig
var x: i32 = 10;
var y: i32 = 20;

swap(&x, &y);
```

After execution:

```text
x = 20
y = 10
```

This pattern is common in systems programming because it avoids unnecessary copying.

## Returning Large Data

Returning large structures repeatedly may be expensive.

Example:

```zig
const BigData = struct {
    values: [10000]u32,
};
```

Instead of returning copies, programs often use pointers or allocators.

Example:

```zig
fn process(data: *BigData) void {

}
```

This allows the function to work directly with existing memory.

## Returning Slices

Functions often return slices.

Example:

```zig
fn firstThree(values: []const i32) []const i32 {
    return values[0..3];
}
```

Calling:

```zig
const data = [_]i32{ 1, 2, 3, 4, 5 };

const result = firstThree(&data);
```

Result:

```text
[1, 2, 3]
```

Slices are lightweight views into memory.

No copying happens here.

## Returning Allocated Memory

Sometimes functions allocate memory dynamically.

Example:

```zig
fn createBuffer(
    allocator: std.mem.Allocator,
) ![]u8 {
    return try allocator.alloc(u8, 1024);
}
```

This pattern is extremely important in Zig.

Notice the allocator parameter:

```zig
allocator: std.mem.Allocator
```

The caller controls memory allocation.

This makes ownership explicit.

## Ownership and Lifetime

When returning memory-related values, lifetime matters.

Safe example:

```zig
fn getMessage() []const u8 {
    return "hello";
}
```

String literals live for the entire program.

Dangerous example:

```zig
fn badSlice() []u8 {
    var data = [_]u8{ 1, 2, 3 };

    return &data;
}
```

This is invalid because `data` disappears when the function exits.

Returning references to dead memory is a serious bug.

Zig tries hard to detect these mistakes.

## Tagged Union Returns

Sometimes functions may produce one of several result types.

Example:

```zig
const Result = union(enum) {
    integer: i32,
    text: []const u8,
};

fn example(flag: bool) Result {
    if (flag) {
        return .{ .integer = 123 };
    }

    return .{ .text = "hello" };
}
```

This pattern is useful when results vary in shape.

You will learn tagged unions later in depth.

## Returning State Objects

Functions often return configuration or state structures.

Example:

```zig
const Config = struct {
    width: u32,
    height: u32,
    fullscreen: bool,
};

fn defaultConfig() Config {
    return .{
        .width = 1280,
        .height = 720,
        .fullscreen = false,
    };
}
```

This style is common in real Zig programs.

## Returning Iterators

Some APIs return iterator objects.

Example conceptually:

```zig
const iterator = list.iterator();
```

The iterator itself stores traversal state.

This avoids returning large collections repeatedly.

## A Complete Example

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

const Stats = struct {
    min: i32,
    max: i32,
    sum: i32,
};

fn analyze(values: []const i32) Stats {
    var min = values[0];
    var max = values[0];
    var sum: i32 = 0;

    for (values) |value| {
        if (value < min) {
            min = value;
        }

        if (value > max) {
            max = value;
        }

        sum += value;
    }

    return .{
        .min = min,
        .max = max,
        .sum = sum,
    };
}

pub fn main() void {
    const values = [_]i32{ 4, 8, 1, 9, 3 };

    const stats = analyze(&values);

    std.debug.print(
        "min={}, max={}, sum={}\n",
        .{
            stats.min,
            stats.max,
            stats.sum,
        },
    );
}
```

Output:

```text
min=1, max=9, sum=25
```

This demonstrates a common Zig pattern:

- compute several related values
- return them together inside a struct

## Mental Model

In Zig, “multiple return values” usually means:

```text
group related data into a structure
```

Instead of special syntax, Zig uses normal language features consistently.

This keeps the language simpler and easier to reason about.

The most important return patterns in Zig are:

| Pattern | Purpose |
|---|---|
| `T` | single value |
| `!T` | value or error |
| `?T` | value or null |
| `struct` | multiple related values |
| `*T` | indirect modification |
| `[]T` | memory view |

You will see these patterns repeatedly throughout Zig codebases.

