# On Static Dispatch vs Dynamic Dispatch

**URL:** <https://ziggit.dev/t/on-static-dispatch-vs-dynamic-dispatch/15662>\
**Category:** Brainstorming\
**Tags:** language, standard-library\
**Created:** [May 22, 2026, 5:04pm UTC](https://ziggit.dev/t/on-static-dispatch-vs-dynamic-dispatch/15662 "2026-05-22T17:04:46Z")\
**Posts on this page:** 1\
**Showing post:** 6

<div class="post-metadata">

**Author:** ![Verafahn](https://ziggit.dev/user_avatar/ziggit.dev/verafahn/32/9285_2.png) [@Verafahn](https://ziggit.dev/u/Verafahn)\
**Post date:** [May 23, 2026, 7:08am UTC](https://ziggit.dev/t/on-static-dispatch-vs-dynamic-dispatch/15662/6 "2026-05-23T07:08:01Z")

</div>

I have found an interface design pattern where static dispatch can coexist with dynamic dispatch.

I can define a trait:

```zig
pub const Trait = struct {
    pub fn method(self: *Trait) void {
        _ = self;
        @compileError("");
    }
};

```

Trait is usually used to describe the contract of a static dispatch interface. I can use some tools to check whether a certain type implements a certain trait.

I also can define an interface:

```zig
pub const Interface = struct {
    vptr: *anyopaque,
    vtable: struct {
        method: fn(self: *anyopaque) void,
    },
};

```

The interface contains virtual pointers and virtual tables, which can be replaced at runtime.

There is no difference between the types that implement this interface. I can write a unified packaging for them:

```zig
pub fn Warpper(comptime T: type) type {
    const dispath_dyn = T == Interface;
    return struct {
        const Self = @This();
        const Impl = if (dispath_dyn) T else *T;
        impl: Impl,

        pub fn from(impl: Impl) Self {
            return .{ .impl = impl };
        }

        pub inline fn method(self: Self) void {
            if (comptime dispath_dyn) {
                self.impl.vtable.method(self.impl.vptr);
            } else {
                @call(.always_inline, T.method, .{self.impl});
            }
        }
    };
}

```

In this way, the caller can use `Warpper(Interface)` to adopt a dynamic dispatch version, or use `Warpper(T)` to adopt a static dispatch version.

For the function parameters that receive this interface, using `anytype` is sufficient:

```zig
pub fn func(warpper: antype) void {
    // ...
}

```

I rewrote the allocator interface of the standard library in this way in `vftrait`. I also tested it, and the results showed that the implementation performance of dynamic dispatch was almost the same as that of the standard library, while the performance of the static dispatch version was higher than that of dynamic dispatch.

---

_[View the full topic](https://ziggit.dev/t/on-static-dispatch-vs-dynamic-dispatch/15662)._
