# `@ptrCast`

### `@ptrCast`

`@ptrCast` converts one pointer type into another pointer type.

It does not move memory. It does not copy memory. It does not change the bytes stored in memory.

It only changes how Zig views the pointer.

That makes it powerful, but also dangerous if used carelessly.

#### The Basic Idea

Suppose you have a pointer to one type:

```zig
const p: *u32 = undefined;
```

Sometimes low-level code needs to view the same address through a different pointer type.

For example:

```zig
const q: *u8 = @ptrCast(p);
```

Now `q` is a pointer to `u8`.

The memory address is the same. The pointer type changed.

You can read this as:

```text
Treat this pointer as a different pointer type.
```

#### Why Pointer Casts Exist

Pointer casts are common in systems programming.

You may need them when working with:

```text
binary formats
network packets
C libraries
operating system APIs
memory-mapped files
hardware registers
custom allocators
serialization code
```

These areas often deal with raw memory. Raw memory does not always arrive with the exact Zig type you want.

For example, a C API may give you a `*anyopaque`, which means “pointer to something, but Zig does not know what.”

You may know that the pointer actually points to a specific struct. In that case, you may use `@ptrCast` to recover the expected pointer type.

#### `@ptrCast` Changes the Pointer Type

Example:

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

const Header = struct {
    magic: u32,
    version: u16,
};

pub fn main() void {
    var header = Header{
        .magic = 0x12345678,
        .version = 1,
    };

    const p: *Header = &header;
    const bytes: [*]u8 = @ptrCast(p);

    std.debug.print("first byte: {}\n", .{bytes[0]});
}
```

Here, `p` points to a `Header`.

Then `bytes` points to the same memory, but treats it as a sequence of bytes.

This kind of code is low-level. It depends on memory layout. It can be useful, but it should be written with care.

#### `@ptrCast` Does Not Convert Values

This is important.

A pointer cast does not convert the value stored at the address.

If memory contains a `u32`, this does not turn the `u32` into four independent safe values in a high-level sense. It only lets you inspect the same memory as bytes.

Compare these two ideas:

```zig
const x: u32 = 100;
const y: u64 = x;
```

This is a numeric conversion. The value `100` becomes a `u64`.

But:

```zig
const p: *u32 = &x;
const q: *u8 = @ptrCast(p);
```

This is a pointer reinterpretation. The address is retyped.

The number stored in memory did not get converted.

#### Alignment Still Matters

Pointer casts do not erase alignment rules.

A `*u32` pointer must point to memory aligned for `u32`.

A `*u8` pointer only needs byte alignment.

Casting from a stricter pointer to a looser pointer is usually easier:

```zig
const p: *u32 = undefined;
const q: *u8 = @ptrCast(p);
```

A `u32` pointer points to memory aligned for `u32`. That address is also valid for `u8`.

The reverse direction is more serious:

```zig
const p: *u8 = undefined;
const q: *u32 = @ptrCast(p);
```

A random `*u8` pointer may not be aligned for `u32`.

If Zig cannot prove the alignment is correct, you may need `@alignCast` too.

Conceptually:

```zig
const q: *u32 = @ptrCast(@alignCast(p));
```

This says two things:

```text
The pointer has the right alignment.
Treat it as a pointer to u32.
```

Use this only when you know the memory really is aligned correctly.

#### Pointer Casts and `anyopaque`

`anyopaque` is Zig’s type for opaque memory.

You often see it in callback APIs and C-style interfaces.

Example:

```zig
const Context = struct {
    count: usize,
};

fn callback(ctx: *anyopaque) void {
    const context: *Context = @ptrCast(@alignCast(ctx));
    context.count += 1;
}
```

Here, the callback receives a generic pointer.

Inside the function, we say:

```text
This generic pointer actually points to Context.
```

This is common when an API wants to pass user data through a generic pointer.

But the cast is a promise. If `ctx` does not really point to a `Context`, the code is wrong.

#### Constness Matters

Pointer casts should respect constness.

If you have a pointer to const data:

```zig
const p: *const u32 = undefined;
```

You should not use `@ptrCast` to turn it into a mutable pointer and modify the data.

That would violate the meaning of `const`.

Correct:

```zig
const q: *const u8 = @ptrCast(p);
```

This keeps the result const.

The pointer type changes, but the pointed-to memory remains read-only through that pointer.

#### Slices Need Extra Care

A slice is not just a pointer.

A slice contains:

```text
pointer
length
```

So this is not the same kind of thing as casting a single pointer:

```zig
[]u8
```

When working with slices, you usually cast the pointer part and handle the length separately.

Example:

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

pub fn main() void {
    var numbers = [_]u32{ 1, 2, 3, 4 };

    const ptr: [*]u8 = @ptrCast(&numbers);
    const byte_len = numbers.len * @sizeOf(u32);

    const bytes = ptr[0..byte_len];

    std.debug.print("byte length: {}\n", .{bytes.len});
}
```

The array has 4 `u32` values.

Each `u32` is 4 bytes.

So the byte slice has length 16.

The pointer cast alone does not compute that length for you.

#### Prefer Standard Library Helpers When Available

For common memory operations, the standard library may provide safer helpers.

For example, when you want the bytes of a value, check the relevant APIs in `std.mem` before writing pointer casts manually.

Manual casts should be rare in beginner code.

A good rule:

Use `@ptrCast` only when you are deliberately working with representation-level memory.

Do not use it just to make a type error disappear.

#### A Dangerous Example

Suppose you write:

```zig
var x: u32 = 123;
const p: *f32 = @ptrCast(&x);
```

This tells Zig to treat the memory of an integer as a floating-point number.

The bits are the same, but the meaning changes completely.

This may be useful in very specific low-level code, but most of the time it is a bug.

For value reinterpretation, `@bitCast` is often clearer than pointer casting, because it works on values rather than addresses.

#### `@ptrCast` vs `@bitCast`

Use `@ptrCast` when you need to reinterpret a pointer.

Use `@bitCast` when you need to reinterpret a value’s bits.

Pointer cast:

```zig
const p: *u32 = &x;
const q: *u8 = @ptrCast(p);
```

Bit cast:

```zig
const bits: u32 = @bitCast(some_f32);
```

The first changes the pointer type.

The second creates a new value with the same bits.

#### How to Read `@ptrCast`

When you see this:

```zig
const q: *SomeType = @ptrCast(p);
```

read it as:

```text
Use the same address as p, but treat it as a pointer to SomeType.
```

Then ask these questions:

```text
Is the address aligned for SomeType?
Does the memory really contain a SomeType?
Is constness preserved?
Is the lifetime still valid?
Is the length handled correctly if slices are involved?
```

These questions matter because the compiler cannot always protect you after you reinterpret raw memory.

#### When Beginners Should Use It

At the beginner level, you should mostly avoid `@ptrCast`.

You may need it when:

```text
calling C APIs
working with opaque callback data
inspecting raw bytes
writing allocators
parsing binary data
interfacing with operating system APIs
```

You usually do not need it for normal application logic.

If ordinary Zig types can express the operation, prefer ordinary Zig types.

#### Key Idea

`@ptrCast` changes one pointer type into another pointer type.

It keeps the same memory address.

It does not convert the stored value.

It does not fix alignment.

It does not prove the memory really has the new type.

Use it when you are intentionally working with raw memory representation, and keep the cast as small and local as possible.

