# Type Coercion

### Type Coercion

Type coercion means Zig converts a value from one type to another when the conversion is safe and well-defined.

For example:

```zig
const x: u8 = 10;
const y: u16 = x;
```

Here, `x` is a `u8`.

`y` is a `u16`.

Zig allows this because every `u8` value can fit inside a `u16`. A `u8` can hold values from `0` to `255`. A `u16` can hold values from `0` to `65535`.

No information is lost.

#### Coercion Is Not Guessing

Zig does not silently convert values in risky ways.

This is not allowed:

```zig
const x: u16 = 1000;
const y: u8 = x; // error
```

A `u16` might contain a value too large for `u8`.

Even though `1000` is clearly too large, the deeper rule is this: Zig does not let a wider integer automatically become a narrower integer.

If you really want a narrowing conversion, you must say so explicitly.

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

Now the cast is visible in the code.

#### Coercion vs Casting

Coercion is automatic.

Casting is explicit.

Coercion:

```zig
const small: u8 = 10;
const large: u16 = small;
```

Casting:

```zig
const large: u16 = 10;
const small: u8 = @intCast(large);
```

The difference matters.

Coercion means the compiler knows the conversion is safe.

Casting means the programmer is asking for a specific conversion.

Good Zig code lets safe conversions stay simple and makes risky conversions visible.

#### Integer Coercion

Integer coercion is allowed when the destination type can represent every possible value of the source type.

This works:

```zig
const a: u8 = 255;
const b: u16 = a;
const c: u32 = b;
```

This also works:

```zig
const a: i8 = -10;
const b: i16 = a;
```

An `i8` can hold values from `-128` to `127`. An `i16` can hold all of those values.

But this does not work:

```zig
const a: i16 = 100;
const b: i8 = a; // error
```

A normal `i16` might not fit into an `i8`.

This also does not work automatically:

```zig
const a: i32 = -1;
const b: u32 = a; // error
```

Signed and unsigned integer conversions can change meaning. Zig requires explicit code.

#### Compile-Time Integers

Integer literals are special.

This works:

```zig
const x: u8 = 100;
```

The literal `100` has no fixed runtime integer type yet. Zig can check at compile time that it fits into `u8`.

This does not work:

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

`300` cannot fit into `u8`.

So literals are flexible, but still checked.

This is allowed:

```zig
const a: u8 = 10;
const b: u16 = 1000;
const c: i32 = -50;
```

Zig chooses the destination type from context and checks the value.

#### Float Coercion

Floating-point coercion can happen from a smaller float type to a larger float type.

```zig
const x: f32 = 1.5;
const y: f64 = x;
```

An `f64` can represent at least as much precision and range as an `f32`.

The reverse is not automatic:

```zig
const x: f64 = 1.5;
const y: f32 = x; // error
```

To narrow a float, use an explicit cast:

```zig
const x: f64 = 1.5;
const y: f32 = @floatCast(x);
```

Again, Zig makes the potentially lossy conversion visible.

#### Integer to Float

Zig does not freely mix integers and floats.

This is not allowed:

```zig
const x: i32 = 10;
const y: f32 = x; // error
```

Use an explicit conversion:

```zig
const x: i32 = 10;
const y: f32 = @floatFromInt(x);
```

Going the other way also requires explicit code:

```zig
const x: f32 = 10.8;
const y: i32 = @intFromFloat(x);
```

This is important because float-to-integer conversion can lose fractional data.

```text
10.8 becomes 10
```

Zig does not hide that.

#### Pointer Coercion

Some pointer conversions are safe and automatic.

For example, a mutable pointer can become a const pointer:

```zig
var number: i32 = 42;

const mutable_ptr: *i32 = &number;
const const_ptr: *const i32 = mutable_ptr;
```

This is safe because `*const i32` is more restrictive. It promises not to mutate through that pointer.

But the reverse is not allowed automatically:

```zig
const number: i32 = 42;

const const_ptr: *const i32 = &number;
const mutable_ptr: *i32 = const_ptr; // error
```

That would remove a safety restriction.

Zig allows coercion when moving toward a safer, more restrictive type. It rejects coercion when it would give code more power than it had before.

#### Array to Slice Coercion

Arrays can coerce to slices.

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

const slice: []const i32 = numbers[0..];
```

The expression:

```zig
numbers[0..]
```

creates a slice that views the whole array.

A slice contains a pointer and a length. It does not copy the array.

You can pass an array slice to a function:

```zig
fn sum(items: []const i32) i32 {
    var total: i32 = 0;

    for (items) |item| {
        total += item;
    }

    return total;
}

pub fn main() void {
    const numbers = [_]i32{ 1, 2, 3, 4 };
    _ = sum(numbers[0..]);
}
```

The function does not need to know the array length at compile time. It receives a slice.

#### String Literal Coercion

A string literal in Zig has an array-like type.

```zig
"hello"
```

In many contexts, it can be used as:

```zig
[]const u8
```

Example:

```zig
const name: []const u8 = "Zig";
```

This is why string literals are convenient in functions that expect byte slices:

```zig
fn greet(name: []const u8) void {
    std.debug.print("hello, {s}\n", .{name});
}

pub fn main() void {
    greet("Zig");
}
```

The string literal is stored as constant bytes, and Zig lets it be viewed as a constant byte slice.

#### Optional Coercion

A plain value can coerce into an optional type.

```zig
const x: i32 = 10;
const maybe_x: ?i32 = x;
```

This is safe. An `i32` value can become “maybe an `i32`” by storing the value case.

You can also write:

```zig
const maybe_name: ?[]const u8 = "Zig";
```

The string is wrapped into the optional.

But the reverse is not automatic:

```zig
const maybe_x: ?i32 = 10;
const x: i32 = maybe_x; // error
```

The optional might be `null`.

You must unwrap it:

```zig
const x: i32 = maybe_x orelse 0;
```

or:

```zig
const x: i32 = maybe_x.?;
```

#### Error Union Coercion

A successful value can coerce into an error union.

```zig
const result: error{Failed}!i32 = 123;
```

This means the result is the success case.

An error can also be stored in the same error union:

```zig
const result: error{Failed}!i32 = error.Failed;
```

But you cannot automatically turn an error union into its payload:

```zig
const result: error{Failed}!i32 = 123;
const value: i32 = result; // error
```

The result might be an error.

You must handle it:

```zig
const value: i32 = result catch 0;
```

or propagate it:

```zig
const value: i32 = try result;
```

#### Error Set Coercion

A smaller error set can coerce into a larger error set.

```zig
const Small = error{
    NotFound,
};

const Large = error{
    NotFound,
    PermissionDenied,
};

fn small() Small!void {
    return error.NotFound;
}

fn large() Large!void {
    return try small();
}
```

This works because every `Small` error is also valid in `Large`.

But the reverse does not work safely:

```zig
fn returnsLarge() Large!void {
    return error.PermissionDenied;
}

fn returnsSmall() Small!void {
    return try returnsLarge(); // error
}
```

`Large` may contain errors that `Small` cannot represent.

#### Comptime Coercion

Compile-time values can often coerce more flexibly because Zig knows their exact value.

```zig
const x: u8 = 255;
```

This works.

```zig
const y: u8 = 256; // error
```

This fails.

Zig is not guessing. It is checking the exact value at compile time.

This also applies inside functions when a value is known at compile time.

```zig
fn make(comptime n: comptime_int) u8 {
    return n;
}
```

Calling:

```zig
const a = make(100);
```

is fine.

Calling:

```zig
const b = make(300);
```

fails because `300` cannot fit in `u8`.

#### Coercion Should Preserve Meaning

The best way to understand Zig coercion is this:

A coercion is allowed when it preserves meaning.

A `u8` value as a `u16` still means the same number.

A mutable pointer as a const pointer is still the same address, but with fewer permissions.

A value as an optional is still the same value, just wrapped in a type that can also hold `null`.

But a `u16` as a `u8` may lose data.

A `f64` as an `f32` may lose precision.

An optional as a plain value may crash if it is `null`.

An error union as a plain value may ignore failure.

Zig makes those conversions explicit.

#### The Main Idea

Type coercion is Zig’s safe automatic conversion system.

It keeps simple, safe code clean:

```zig
const small: u8 = 10;
const large: u16 = small;
```

But it rejects conversions that may lose information, hide failure, remove const safety, or ignore absence.

For those, you must write an explicit cast, unwrap, `try`, or `catch`.

That is the rule to remember:

```text
Safe conversions may be automatic.
Risky conversions must be explicit.
```

