Why casting an empty slice to a pointer isn't checked?

Hi everyone!

I noticed that casting an empty slice to a pointer doesn’t produce an error by itself.
For example, the following code:

const std = @import("std");

pub fn getSlice() []i32 {
    return &.{};
}

pub fn main() void {
    const foo: []i32 = getSlice();
    const val: *i32 = @ptrCast(foo);
    std.debug.print("Hello, {d}!\n", .{val.*});
}

crashes on print with General protection exception. I wonder why it’s not @ptrCast that fails?

Hi @tekhnus

It crashes when you dereference the pointer (val.*).

The problem is that you are casting to get the pointer.
If you use foo.ptr you get:

error: expected type '*i32', found '[*]i32'
    const val: *i32 = foo.ptr;
                      ~~~^~~~
note: a many pointer cannot cast into a single pointer

Zig compiler complains because [*]i32 can have zero elements.
Zig compiler cannot complain when calling @ptrCast because this is a no-operation to signal the compiler that you know what you are doing (e.g. you just checked that foo.len > 0).

Welcome to ziggit :slight_smile:

1 Like

Zig compiler cannot complain when calling @ptrCast

I understand that. The question is, why don’t we get a runtime safety check in debug build mode? I suppose that performing such cast is undefined behavior (analogous to casting a null pointer to *i32 which is undefined behaviour and causes cast causes pointer to be null at runtime)?

1 Like

I agree that casting a zero-item slice to a single-item pointer can be zig undefined behavior.

1 Like