Unable to cast pointer to opaque

I’m working with a C library which returns a pointer. Zig translates that pointer as ?*opaque{}, as it’s basically used as a handle.

I’m trying to assign that handle to a Zig struct field, which itself is a ?*opaque{} since I want to keep cImport contained to a separate file.

However, despite both sides being ?*opaque{}, I get an error when trying to make the assignment.

Here’s a simplified example:

const std = @import("std");
const builtin = @import("builtin");

const CType = opaque{};

fn cFunction() ?*CType {
    return null;
}

const MyData = struct {
    native: ?*opaque{},
};

pub fn main() void {
    var data: MyData = undefined;
    data.native = cFunction();
}

Error:

example.zig:16:28: error: expected type '?*example.MyData.MyData__opaque_3335', found '?*example.CType'
example.zig:16:28: note: pointer type child 'example.CType' cannot cast into pointer type child 'example.MyData.MyData__opaque_3335'
example.zig:4:15: note: opaque declared here
example.zig:11:15: note: opaque declared here

I tried using @ptrCast() or @as() to cast the result to ?*opaque{}, but of course that doesn’t work.

How do I get Zig to accept the assignment?

Each declaration of an opaque type is distinct and will not coerce to a different opaque. The opaque in const CType = opaque {} is a different type from the one in native: ?*opaque {}. This is similar to how two struct types with the exact same fields and decls are still considered distinct types.

If you change the type of the native field to ?*CType, they will be one and the same type and your code will work as is.

The alternative is to change the data.native = cFunction() line to data.native = @ptrCast(cFunction()), but if the types truly represent the same type, it would probably make the most sense to declare them both as ?*CType.

1 Like

Definitely true. For the real code, CType comes from cImport in a different file, so it is more like ?*c.wl_display, and I don’t want to sprinkle cImport types throughout the codebase.

But your comment gave me an idea to create a separate type, const BackendPtr = *opaque{}, which @ptrCast is happy to cast to.

Thanks for the insight!