# Type Inference

### Type Inference

Type inference means Zig can figure out a type from the value you write.

Instead of writing this:

```zig
const age: i32 = 30;
```

you can often write this:

```zig
const age = 30;
```

Zig looks at the value `30` and works out what type it should have.

This keeps simple code short. You do not need to write obvious types everywhere.

#### A first example

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

pub fn main() void {
    const name = "Zig";
    const year = 2026;
    const stable = false;

    std.debug.print("{s} {}\n", .{ name, year });
    std.debug.print("stable release: {}\n", .{stable});
}
```

Zig can infer the types of `name`, `year`, and `stable`.

The code does not say:

```zig
const stable: bool = false;
```

because `false` is already clearly a boolean.

The code does not say:

```zig
const name: some_string_type = "Zig";
```

because string literal types are more detailed than beginners need at first. Zig can infer them for local values.

#### Inference is not guessing

Type inference is not magic. Zig does not guess randomly.

It follows rules.

This value is a boolean:

```zig
const ok = true;
```

This value is a floating point literal:

```zig
const pi = 3.14;
```

This value is a string literal:

```zig
const language = "Zig";
```

This value is an array:

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

The compiler reads the expression on the right side and assigns a type to the name on the left side.

#### You can still write the type

Type inference is optional.

You can write the type explicitly whenever it improves clarity.

```zig
const age: u8 = 30;
const score: i32 = 100;
const price: f64 = 19.99;
const enabled: bool = true;
```

This is often better when the exact type matters.

For example, this tells the reader that `age` is stored as an unsigned 8-bit integer:

```zig
const age: u8 = 30;
```

This tells the reader that `price` uses 64-bit floating point:

```zig
const price: f64 = 19.99;
```

Good Zig code uses both styles. It infers types when the type is obvious, and writes types when the type is important.

#### Integer literals are special

Integer literals are flexible.

```zig
const x = 10;
```

At first, `10` can behave like a compile-time integer. It can later fit into a specific integer type if the value is valid for that type.

For example:

```zig
const a: u8 = 10;
const b: i32 = 10;
const c: u64 = 10;
```

The same literal `10` can fit into all of these types.

But this fails:

```zig
const x: u8 = 300; // error
```

The type `u8` can store only values from `0` to `255`.

So Zig accepts integer literals only when the value fits the target type.

#### Floating point literals are also flexible

Floating point literals can also be used with different floating point types.

```zig
const a: f32 = 3.14;
const b: f64 = 3.14;
```

The value is converted to the target floating point type.

When precision matters, write the type explicitly:

```zig
const distance: f64 = 12345.6789;
```

This avoids leaving an important representation choice hidden.

#### Inference from function return types

Zig can infer a local variable’s type from a function call.

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

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

The function `add` returns `i32`, so `result` has type `i32`.

You do not need to write:

```zig
const result: i32 = add(10, 20);
```

unless you want to make it extra clear.

#### Inference from arrays

Arrays often use inference.

```zig
const numbers = [_]u8{ 10, 20, 30 };
```

The `[_]u8` part means:

```text
array of u8 values, with length inferred
```

Zig counts the elements and creates an array of length 3.

The type is:

```zig
[3]u8
```

So this:

```zig
const numbers = [_]u8{ 10, 20, 30 };
```

means:

```text
create an array of 3 u8 values
```

You can also write the length manually:

```zig
const numbers: [3]u8 = .{ 10, 20, 30 };
```

Both forms are useful.

#### Inference with `var`

Type inference works with `var` too.

```zig
var count = 0;
```

But be careful.

A mutable variable must have a concrete type. If you later assign a value that does not fit, the code fails.

```zig
var count: u8 = 0;
count = 255; // ok
count = 256; // error
```

When using `var`, it is often clearer to write the type:

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

This is common for indexes and counters.

#### Inference does not mean type changes

Once a variable has a type, that type does not change.

```zig
var x: i32 = 10;
x = 20;    // ok
x = true;  // error
```

The name `x` was declared as `i32`. It can hold another `i32`, but it cannot suddenly become a boolean.

Even when the type is inferred, the rule is the same:

```zig
var x = @as(i32, 10);
x = 20;    // ok
x = true;  // error
```

A variable has one type for its whole lifetime.

#### Using `@as`

The builtin `@as` tells Zig to treat a value as a specific type.

```zig
const x = @as(u32, 10);
```

This means:

```text
make x a u32 with value 10
```

This is useful when Zig needs more type information.

Example:

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

pub fn main() void {
    const x = @as(u8, 42);
    std.debug.print("{}\n", .{x});
}
```

Here, `x` is clearly a `u8`.

#### When inference is helpful

Inference is helpful for local, obvious values.

```zig
const name = "Ada";
const enabled = true;
const result = add(1, 2);
```

These are easy to read without explicit types.

Inference is also useful when the type is long or noisy.

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

You do not want to write the full type of `std`. Zig handles it.

#### When explicit types are better

Write the type when it is part of the meaning.

```zig
const port: u16 = 8080;
const file_size: u64 = 1_000_000;
const index: usize = 0;
const temperature: f64 = 21.5;
```

These types communicate intent.

A port number is usually a 16-bit unsigned integer.

An index is commonly `usize`.

A temperature using decimal measurement is reasonably `f64`.

Explicit types are also better in public interfaces:

```zig
pub fn connect(port: u16) void {
    _ = port;
}
```

Function parameters and return types should be clear because other code depends on them.

#### Do not fight the compiler

Sometimes beginners try to force inference too far.

This may be unclear:

```zig
var value = undefined;
```

Zig cannot infer a useful type from `undefined` alone.

Write the type:

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

Better yet, initialize it immediately when possible:

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

Zig wants enough information to prove the program is well-typed.

#### A complete example

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

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

pub fn main() void {
    const name = "Zig";
    const version_major: u8 = 0;
    const version_minor: u8 = 16;

    var counter: usize = 0;
    counter += 1;

    const result = square(12);

    std.debug.print("{s} version {}.{}\n", .{
        name,
        version_major,
        version_minor,
    });

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

In this example, some values use inference:

```zig
const name = "Zig";
const result = square(12);
```

Some values use explicit types:

```zig
const version_major: u8 = 0;
const version_minor: u8 = 16;
var counter: usize = 0;
```

Both choices are deliberate.

#### The main idea

Type inference lets Zig remove unnecessary type annotations without weakening the type system.

The compiler still knows the exact type. The program is still statically typed. The variable still cannot change into another kind of value.

Use inference when the type is obvious. Write the type when size, meaning, API clarity, or memory layout matters.

