# Zig Generics Use `comptime`

### Generic Functions

A generic function is a function that works with many types instead of only one type.

Without generics, you often duplicate code.

Example:

```zig
fn addI32(a: i32, b: i32) i32 {
    return a + b;
}
```

Then later:

```zig
fn addF64(a: f64, b: f64) f64 {
    return a + b;
}
```

The logic is identical.

Only the types change.

Generic functions solve this problem.

## Zig Generics Use `comptime`

Zig implements generics through compile-time programming.

The most common pattern uses:

```zig
comptime
```

Example:

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

This function works with many types.

## Calling Generic Functions

Example:

```zig
const x = add(i32, 10, 20);
const y = add(f64, 1.5, 2.5);
```

Results:

```text
x = 30
y = 4.0
```

The compiler generates specialized versions for each type.

Conceptually:

```text
add(i32, ...)
  -> integer version

add(f64, ...)
  -> floating-point version
```

## Understanding `comptime T: type`

This line:

```zig
comptime T: type
```

means:

- `T` is known at compile time
- `T` itself is a type

The caller passes a type as input.

Example:

```zig
add(i32, 1, 2)
```

Here:

```text
T = i32
```

The compiler creates a version specialized for `i32`.

## Why Zig Uses Compile-Time Generics

Many languages implement generics differently.

Examples:

| Language | Generic System |
|---|---|
| C++ | templates |
| Rust | monomorphization + traits |
| Java | type erasure |
| Go | type parameters |
| Zig | compile-time execution |

Zig’s system is unusually flexible because generics are built from normal compile-time language features.

## A Generic Maximum Function

Example:

```zig
fn max(
    comptime T: type,
    a: T,
    b: T,
) T {
    return if (a > b) a else b;
}
```

Calling:

```zig
const a = max(i32, 10, 20);
const b = max(f64, 1.5, 0.5);
```

Results:

```text
a = 20
b = 1.5
```

## Generic Arrays

Generics work well with arrays.

Example:

```zig
fn first(
    comptime T: type,
    values: []const T,
) T {
    return values[0];
}
```

Calling:

```zig
const ints = [_]i32{ 1, 2, 3 };
const floats = [_]f64{ 1.5, 2.5 };

const a = first(i32, &ints);
const b = first(f64, &floats);
```

The same logic works for many element types.

## Generic Structs

Generics are not limited to functions.

Structs can also be generic.

Example:

```zig
fn Box(comptime T: type) type {
    return struct {
        value: T,
    };
}
```

Using it:

```zig
const IntBox = Box(i32);

const box = IntBox{
    .value = 123,
};
```

This generates a custom structure specialized for `i32`.

## Generic Data Structures

Generic structs are extremely important.

Examples:

- lists
- hash maps
- queues
- trees
- allocators
- buffers

The Zig standard library uses generics heavily.

## Generic Printing

Example:

```zig
fn printValue(value: anytype) void {
    std.debug.print("{}\n", .{value});
}
```

This uses:

```zig
anytype
```

which allows automatic generic parameter inference.

Calling:

```zig
printValue(123);
printValue(3.14);
printValue(true);
```

The compiler determines the types automatically.

## `anytype`

`anytype` is a convenient shorthand.

Instead of:

```zig
fn print(
    comptime T: type,
    value: T,
)
```

you can write:

```zig
fn print(value: anytype)
```

This is common in Zig APIs.

## Type Inference

Example:

```zig
fn identity(value: anytype) @TypeOf(value) {
    return value;
}
```

Calling:

```zig
const x = identity(10);
const y = identity(3.5);
```

The compiler infers types automatically.

## Compile-Time Specialization

Each type creates a specialized version.

Conceptually:

```text
add(i32, ...)
  -> generated code #1

add(f64, ...)
  -> generated code #2
```

This often produces very efficient machine code.

## Generic Constraints

Sometimes not all types are valid.

Example:

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

This works for numeric types.

But calling:

```zig
add([]const u8, "a", "b");
```

fails because strings cannot use `+` this way.

The compiler checks operations during specialization.

## Compile-Time Type Inspection

Zig can inspect types at compile time.

Example:

```zig
fn describe(
    comptime T: type,
) void {
    std.debug.print(
        "{any}\n",
        .{@typeInfo(T)},
    );
}
```

Calling:

```zig
describe(i32);
```

This is part of Zig’s reflection system.

## Generic Algorithms

Generic algorithms are one of the biggest uses of generics.

Example conceptual algorithms:

- sorting
- searching
- hashing
- parsing
- serialization

One algorithm can support many types.

## Generic Memory Utilities

Example conceptual function:

```zig
fn swap(
    comptime T: type,
    a: *T,
    b: *T,
) void {

}
```

This can swap:

- integers
- floats
- structs
- arrays

without duplicating code.

## Generics and Zero-Cost Abstractions

Zig generics are designed for high performance.

The compiler generates specialized code directly.

This avoids many runtime costs.

Conceptually:

```text
generic abstraction
  ->
specialized machine code
```

This is often called a zero-cost abstraction.

## Generic Iteration Example

```zig
fn printAll(values: anytype) void {
    for (values) |value| {
        std.debug.print(
            "{}\n",
            .{value},
        );
    }
}
```

Calling:

```zig
const numbers = [_]i32{ 1, 2, 3 };

printAll(numbers);
```

The compiler adapts the function automatically.

## Generic Errors

Generic code can produce confusing compiler messages initially.

Example:

```zig
fn test(value: anytype) void {
    value.invalidMethod();
}
```

Compiler errors may appear during specialization instead of initial parsing.

This is normal in compile-time generic systems.

## Generics vs Runtime Polymorphism

Generics:

```text
compile-time specialization
```

Function pointers/interfaces:

```text
runtime polymorphism
```

Generics usually produce faster code because decisions happen during compilation.

Runtime polymorphism is more dynamic but may cost more at runtime.

## A Complete Example

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

fn swap(
    comptime T: type,
    a: *T,
    b: *T,
) void {
    const temp = a.*;
    a.* = b.*;
    b.* = temp;
}

pub fn main() void {
    var x: i32 = 10;
    var y: i32 = 20;

    swap(i32, &x, &y);

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

    var a: f64 = 1.5;
    var b: f64 = 2.5;

    swap(f64, &a, &b);

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

Output:

```text
20 10
2.5 1.5
```

One function works for multiple types safely and efficiently.

## Mental Model

Generic functions mean:

```text
write logic once
use it with many types
```

Zig achieves this through compile-time specialization.

The core ideas are:

| Feature | Purpose |
|---|---|
| `comptime` | compile-time parameters |
| `type` | types as values |
| `anytype` | inferred generic parameters |
| specialization | generated type-specific code |

Generics are one of Zig’s most powerful features because they combine:

- flexibility
- strong type safety
- compile-time checking
- high performance

without requiring a separate template language or heavy runtime system.

