# `@intCast`

### `@intCast`

`@intCast` converts one integer value to another integer type.

You use it when Zig needs an explicit promise that the integer value fits in the destination type.

```zig
const small: u8 = @intCast(big);
```

This says:

```text
Convert big to u8.
```

But the value must fit in `u8`.

A `u8` can store values from `0` to `255`.

#### Why `@intCast` Exists

Integer conversions can lose information.

Example:

```zig
const big: u16 = 300;
const small: u8 = big;
```

This is not allowed.

Why? Because `300` cannot fit in `u8`.

If Zig silently allowed this, the program could produce a wrong value.

So Zig requires an explicit cast:

```zig
const big: u16 = 200;
const small: u8 = @intCast(big);
```

This is allowed because `200` fits in `u8`.

#### The Destination Type Comes from Context

In modern Zig, `@intCast` does not take the destination type as an argument.

You write:

```zig
const small: u8 = @intCast(big);
```

The destination type is `u8`, because the variable says:

```zig
const small: u8
```

You can also provide the type with `@as`:

```zig
const small = @as(u8, @intCast(big));
```

This means the same thing, but it is more explicit.

#### Compile-Time Checking

If the compiler knows the value does not fit, it rejects the code.

```zig
const x: u16 = 300;
const y: u8 = @intCast(x);
```

Since `x` is known at compile time, Zig can see that `300` does not fit in `u8`.

A valid example:

```zig
const x: u16 = 200;
const y: u8 = @intCast(x);
```

This works.

#### Runtime Checking

Sometimes the value is known only when the program runs.

```zig
fn shrink(x: u16) u8 {
    return @intCast(x);
}
```

This function says: convert `x` to `u8`.

But not every `u16` fits in `u8`.

In safe build modes, Zig checks the value at runtime. If the value is too large, the program traps.

A safer version returns an error:

```zig
fn shrink(x: u16) !u8 {
    if (x > 255) return error.TooLarge;
    return @intCast(x);
}
```

Now the function handles the problem directly.

#### Signed and Unsigned Integers

`@intCast` also handles signedness changes.

Example:

```zig
const x: i32 = 100;
const y: u32 = @intCast(x);
```

This works because `100` can be represented as `u32`.

This does not work safely:

```zig
const x: i32 = -1;
const y: u32 = @intCast(x);
```

A negative value cannot be represented as an unsigned integer.

A safe version checks first:

```zig
fn toUnsigned(x: i32) !u32 {
    if (x < 0) return error.Negative;
    return @intCast(x);
}
```

#### Widening Usually Does Not Need `@intCast`

If the destination type can represent every value of the source type, Zig can often convert without `@intCast`.

Example:

```zig
const small: u8 = 200;
const big: u16 = small;
```

Every `u8` value fits in `u16`, so this is safe.

But narrowing needs a cast:

```zig
const big: u16 = 200;
const small: u8 = @intCast(big);
```

Narrowing means converting to a type with a smaller range.

#### `@intCast` Is Not `@truncate`

`@intCast` checks that the value fits.

`@truncate` keeps only the low bits and discards the rest.

Example:

```zig
const x: u16 = 300;
const y: u8 = @truncate(x);
```

`300` in hexadecimal is:

```text
0x012c
```

The low 8 bits are:

```text
0x2c
```

So `y` becomes `44`.

That is not a safe numeric conversion. It is bit-level truncation.

Use `@intCast` when you want the same numeric value in a different integer type.

Use `@truncate` when you intentionally want to discard high bits.

#### Common Example: Indexes

Many lengths and indexes use `usize`.

But sometimes an API expects a smaller integer type.

```zig
fn writeByteCount(count: usize) !u8 {
    if (count > 255) return error.TooLarge;
    return @intCast(count);
}
```

This is clear:

```text
The input may be large.
The output must fit in one byte.
The function checks before casting.
```

#### Common Example: File Formats

Binary file formats often use fixed-size integer fields.

For example, a format may store a length as `u16`.

```zig
fn encodeLength(len: usize) !u16 {
    if (len > std.math.maxInt(u16)) {
        return error.LengthTooLarge;
    }

    return @intCast(len);
}
```

This avoids silent overflow.

The cast is correct because the function checks the range first.

#### Using `std.math.maxInt`

Instead of writing `255`, use `std.math.maxInt` for clarity:

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

fn shrink(x: u16) !u8 {
    if (x > std.math.maxInt(u8)) return error.TooLarge;
    return @intCast(x);
}
```

This is better because the limit is tied to the destination type.

For signed types, you may also use `std.math.minInt`.

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

fn toI8(x: i32) !i8 {
    if (x < std.math.minInt(i8)) return error.TooSmall;
    if (x > std.math.maxInt(i8)) return error.TooLarge;
    return @intCast(x);
}
```

#### `@intCast` Does Not Change Meaning

`@intCast` is a numeric conversion.

It tries to preserve the number.

Example:

```zig
const x: u16 = 42;
const y: u8 = @intCast(x);
```

Both `x` and `y` mean the number `42`.

This is different from `@bitCast`, which preserves raw bits.

For integer type changes, prefer `@intCast` unless you truly need bit-level behavior.

#### How to Read `@intCast`

When you see:

```zig
const y: T = @intCast(x);
```

read it as:

```text
Convert integer x to type T, and require that the value fits.
```

Then ask:

```text
Can every possible x fit in T?
If not, where is the range check?
Should this function return an error instead of trapping?
```

That habit prevents many integer bugs.

#### Key Idea

`@intCast` converts an integer to another integer type while preserving the numeric value.

It is used when the conversion may be unsafe without a range check.

Use it for explicit integer narrowing or signedness changes. Check the range yourself when invalid input is possible.

