# A Simple Function

### Inline Functions

An inline function is a function where the compiler may place the function’s code directly at the call site instead of performing a normal function call.

Normally, calling a function works like this:

```text
caller
  -> jump to function
  -> run function
  -> return back
```

Inlining changes this.

Instead of jumping to the function, the compiler copies the function body directly into the caller.

Conceptually:

```text
replace function call with function code
```

Inlining is mainly a performance optimization.

## A Simple Function

Example:

```zig
fn square(x: i32) i32 {
    return x * x;
}
```

Using it:

```zig
const result = square(5);
```

Normally, this creates a function call.

With inlining, the compiler may transform it conceptually into:

```zig
const result = 5 * 5;
```

No actual function call happens at runtime.

## Why Function Calls Cost Something

A normal function call usually involves:

- saving return information
- jumping to another memory location
- creating a stack frame
- returning afterward

Modern CPUs are fast, but function calls still have overhead.

Inlining removes much of this overhead.

## Zig and Inlining

Zig gives the compiler strong freedom to optimize.

The compiler may inline small functions automatically.

You can also request inlining explicitly.

Example:

```zig
inline fn square(x: i32) i32 {
    return x * x;
}
```

The keyword:

```zig
inline
```

requests inlining.

## Inline Is a Request

Important:

```text
inline does not mean “guaranteed faster”
```

Inlining can improve performance, but excessive inlining can also increase binary size.

Large binaries may reduce instruction-cache efficiency.

Optimization always involves tradeoffs.

## A Complete Example

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

inline fn add(a: i32, b: i32) i32 {
    return a + b;
}

pub fn main() void {
    const result = add(10, 20);

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

Output:

```text
30
```

The program behaves normally whether the compiler inlines the function or not.

Inlining changes implementation details, not program meaning.

## Inline Functions and Small Helpers

Inlining is most useful for:

- tiny helper functions
- math operations
- wrappers
- hot code paths
- compile-time specialization

Example:

```zig
inline fn max(a: i32, b: i32) i32 {
    return if (a > b) a else b;
}
```

This is a good inline candidate because the function body is extremely small.

## Large Inline Functions

Large functions are poor inline candidates.

Bad example:

```zig
inline fn processHugeDatabase() void {

    // 2000 lines

}
```

Inlining very large functions can:

- increase binary size dramatically
- reduce cache efficiency
- increase compile times

Inlining is usually best for small operations.

## Inline Loops

Zig also supports inline loops.

Example:

```zig
inline for (.{ 1, 2, 3 }) |value| {
    std.debug.print("{}\n", .{value});
}
```

This loop executes at compile time.

The compiler expands it conceptually into:

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

Inline loops are extremely important in Zig metaprogramming.

## Inline Branches

Example:

```zig
inline if (condition) {

}
```

This allows compile-time branch evaluation.

Usually this appears with `comptime`.

## Compile-Time Specialization

Inlining is closely related to compile-time programming.

Example:

```zig
fn add(
    comptime T: type,
    a: T,
    b: T,
) T {
    return a + b;
}
```

The compiler generates specialized versions for each type.

Example calls:

```zig
add(i32, 1, 2);
add(f64, 1.5, 2.5);
```

This often combines naturally with inlining.

## Inlining and Generics

Generic code frequently becomes inline-expanded.

Why?

Because specialized code is easier to optimize aggressively.

This is one reason Zig generic abstractions can remain very fast.

## Function Call Overhead Example

Suppose:

```zig
fn increment(x: i32) i32 {
    return x + 1;
}
```

Used millions of times:

```zig
while (i < 1000000) : (i += 1) {
    total += increment(i);
}
```

Inlining may remove repeated function-call overhead.

The compiler can often optimize further afterward.

## Inlining Enables Other Optimizations

Inlining is powerful because it exposes more code to the optimizer.

Example:

```zig
inline fn multiplyByTwo(x: i32) i32 {
    return x * 2;
}
```

After inlining:

```zig
value * 2
```

The compiler may then simplify further.

Without inlining, some optimizations are harder.

## Recursive Inline Functions

Inlining recursive functions is difficult.

Example:

```zig
inline fn factorial(n: u32) u32 {
    if (n == 0) {
        return 1;
    }

    return n * factorial(n - 1);
}
```

Unlimited expansion is impossible.

Compilers must handle recursion carefully.

## Too Much Inlining

Excessive inlining can hurt performance.

Possible problems:

| Problem | Explanation |
|---|---|
| Larger binaries | more duplicated code |
| Worse cache behavior | instruction cache pressure |
| Longer compile times | more generated machine code |
| Harder debugging | call structure disappears |

Inlining is not automatically beneficial everywhere.

## Debugging Inline Functions

Inlining can make debugging harder.

Why?

Because the original function call may disappear after optimization.

Stepping through code in a debugger may feel different between:

- debug builds
- optimized builds

This is normal in systems programming.

## Inline and Readability

Do not inline functions just because they are small.

This is unnecessary:

```zig
inline fn getZero() i32 {
    return 0;
}
```

Inlining should solve a real problem.

Usually:

```text
clarity first
performance second
```

Measure performance before optimizing aggressively.

## Zig Often Optimizes Automatically

Modern Zig compilers already perform many automatic optimizations.

Small functions are frequently inlined automatically even without the `inline` keyword.

Therefore:

```text
explicit inline is often unnecessary
```

Use it deliberately.

## Inline and `comptime`

One of Zig’s most important ideas:

```text
compile-time execution
```

Inlining often interacts with `comptime`.

Example:

```zig
inline for (@typeInfo(T).Struct.fields)
    |field|
{

}
```

This allows code generation during compilation.

You will encounter this style heavily in advanced Zig code.

## A Complete Example

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

inline fn clamp(
    value: i32,
    min: i32,
    max: i32,
) i32 {
    if (value < min) {
        return min;
    }

    if (value > max) {
        return max;
    }

    return value;
}

pub fn main() void {
    const a = clamp(5, 0, 10);
    const b = clamp(-5, 0, 10);
    const c = clamp(50, 0, 10);

    std.debug.print(
        "{} {} {}\n",
        .{ a, b, c },
    );
}
```

Output:

```text
5 0 10
```

This function is a good inline candidate because:

- very small
- called frequently
- performance-sensitive
- simple branching

## Mental Model

Inlining means:

```text
replace a function call with the function body itself
```

Instead of:

```text
jump to function
```

the compiler may produce:

```text
direct embedded instructions
```

Inlining can:

- reduce function-call overhead
- enable more optimizations
- improve performance

But excessive inlining can also create larger, slower binaries.

Good systems programming balances:

- readability
- maintainability
- performance
- code size

Zig gives you explicit control when needed, while still allowing the compiler to optimize aggressively automatically.

