# Opaque Types

### Opaque Types

An opaque type is a type whose internal structure is hidden.

In Zig, you write an opaque type with `opaque {}`.

```zig
const Handle = opaque {};
```

This defines a type named `Handle`, but it does not say what fields `Handle` has.

You cannot create a normal `Handle` value directly. You also cannot access fields inside it. The whole point is that the inside is unknown.

Opaque types are mostly used through pointers.

```zig
*Handle
```

This means:

```text
a pointer to a Handle
```

You know that a `Handle` exists somewhere, but you do not know its layout.

#### Why Opaque Types Exist

Opaque types are useful when one part of a program should not know the details of another part.

This is common in C libraries.

A C library might give you a database handle, window handle, file handle, parser handle, or graphics context. You can pass the handle around, but you are not supposed to inspect its fields.

The library owns the real data. Your code only holds a pointer to it.

In Zig, an opaque type models that clearly.

```zig
const Database = opaque {};

extern fn db_open(path: [*:0]const u8) ?*Database;
extern fn db_close(db: *Database) void;
```

Here, `Database` is opaque.

The user of the API can open a database and receive `*Database`.

But the user cannot do this:

```zig
db.some_field // error
```

There are no visible fields.

#### Opaque Types Hide Layout

A normal struct exposes its layout:

```zig
const User = struct {
    id: u64,
    name: []const u8,
};
```

Code can access the fields:

```zig
const id = user.id;
const name = user.name;
```

An opaque type does not expose layout:

```zig
const UserHandle = opaque {};
```

There is no `.id`.

There is no `.name`.

There is no known size that ordinary Zig code can use to create the value directly.

Usually, you only use pointers to opaque values:

```zig
*UserHandle
?*UserHandle
*const UserHandle
```

This is the same idea as an incomplete struct type in C.

#### A Small C-Style API

Consider a tiny imaginary C library:

```c
typedef struct App App;

App *app_create(void);
void app_destroy(App *app);
void app_run(App *app);
```

The C header says `App` exists, but it does not reveal the fields of `struct App`.

In Zig, you can represent that like this:

```zig
const App = opaque {};

extern fn app_create() ?*App;
extern fn app_destroy(app: *App) void;
extern fn app_run(app: *App) void;
```

Then you can use it:

```zig
pub fn main() void {
    const app = app_create() orelse return;
    defer app_destroy(app);

    app_run(app);
}
```

This code does not know how `App` is stored. It only knows how to ask the external library to create, run, and destroy it.

That is exactly what we want.

#### Opaque Types and Ownership

Opaque types often represent resources.

A pointer to an opaque value may mean:

```text
an open database connection
a window created by a graphics library
a parser object
a compression stream
a network session
a handle owned by another library
```

Because the layout is hidden, the code that receives the pointer usually cannot free the memory directly. It must call the matching destroy or close function.

For example:

```zig
extern fn parser_create() ?*Parser;
extern fn parser_destroy(parser: *Parser) void;
```

Correct use:

```zig
const parser = parser_create() orelse return;
defer parser_destroy(parser);
```

This pattern is common:

```text
create resource
defer cleanup
use resource
```

The opaque type helps enforce the boundary. Your code cannot accidentally modify the internals because the internals are not visible.

#### Opaque Types vs Structs

Use a struct when Zig code should know the fields.

```zig
const Point = struct {
    x: f32,
    y: f32,
};
```

This is good because a point is simple data. Code should be able to read `x` and `y`.

Use an opaque type when Zig code should not know the fields.

```zig
const Window = opaque {};
```

This is good when the real window data belongs to a library or module that controls it.

A struct says:

```text
Here is the data layout.
You may access it.
```

An opaque type says:

```text
This thing exists.
You may hold a pointer to it.
You may not inspect its internals.
```

#### Opaque Types vs `anyopaque`

Zig also has `anyopaque`.

These two are related, but they are not the same.

```zig
const FileHandle = opaque {};
```

This creates a specific hidden type.

A `*FileHandle` pointer is a pointer to that specific hidden type.

By contrast:

```zig
*anyopaque
```

means a pointer to some unknown data of no specific type.

You can think of `*anyopaque` as Zig’s version of C’s `void *`.

Use a named opaque type when the hidden thing has a specific identity.

```zig
const Database = opaque {};
const Window = opaque {};
```

Now these are different types:

```zig
*Database
*Window
```

The compiler will not confuse them.

That is useful. A database handle and a window handle are both hidden pointers, but they are not the same kind of handle.

#### Example: Safer Handles

Suppose two libraries expose two different resources:

```zig
const Database = opaque {};
const Window = opaque {};

extern fn db_close(db: *Database) void;
extern fn window_close(window: *Window) void;
```

This is safe:

```zig
db_close(database);
window_close(window);
```

This is a type error:

```zig
db_close(window); // error
```

Even though both are pointers to hidden things, their types are different.

Named opaque types give you type safety across API boundaries.

#### Opaque Types Are Usually Incomplete Alone

You usually do not write code like this:

```zig
var h: Handle = undefined; // not useful
```

Opaque values are not meant to be stored directly by normal Zig code.

You usually store pointers:

```zig
var h: ?*Handle = null;
```

or receive them from functions:

```zig
const h = createHandle() orelse return;
```

The actual storage is created somewhere else.

That “somewhere else” might be:

```text
a C library
an operating system API
a Zig module that hides its implementation
a custom allocator inside another subsystem
```

#### Opaque Types in Your Own APIs

Opaque types are not only for C interop. You can also use them to design Zig APIs with hidden implementation details.

For example, a module might expose this:

```zig
pub const Engine = opaque {};

pub fn create() !*Engine {
    // implementation hidden
}

pub fn destroy(engine: *Engine) void {
    // implementation hidden
}

pub fn update(engine: *Engine) void {
    // implementation hidden
}
```

The user can call:

```zig
const engine = try engine_lib.create();
defer engine_lib.destroy(engine);

engine_lib.update(engine);
```

But the user cannot access the internal fields of `Engine`.

This gives you freedom to change the internal layout later without changing the public API.

#### What Beginners Should Remember

An opaque type is for hidden data.

You usually use it through a pointer.

```zig
const Thing = opaque {};
```

Common pointer forms are:

```zig
*Thing
?*Thing
*const Thing
```

Use opaque types when the caller should know that a type exists, but should not know how it is built inside.

They are especially common when wrapping C libraries or exposing stable APIs.

The main idea is simple:

```text
A struct reveals its fields.
An opaque type hides them.
```

