Zig NEWS

Pyrolistical
Pyrolistical

Posted on

How to escape Python and write more Zig

We love and know Zig. But Python? Me personally not so much. While Python isn't the hardest language to learn, I am far more productive in Zig.

Fortunately, I can escape Python and mainly work in Zig as both support the C ABI.

Python's standard library directly supports the C ABI via ctypes, but it is a bit clunky to use. A far more user friendly library is cffi.

Calling Zig via cffi

Let's say we have a simple sqrt function written in Zig.

// simple.zig

export fn sqrt(x: f64) f64 {
    return @sqrt(x);
}
Enter fullscreen mode Exit fullscreen mode

We can build this as a library:

zig build-lib -dynamic simple.zig
Enter fullscreen mode Exit fullscreen mode

In Python land, we use cffi to declare the sqrt function, import the library and call it. Note the following assumes the code is being run on Windows.

from cffi import FFI

cffi = FFI()
cffi.cdef(
    """
      double sqrt(double x);
    """
)

import os

simple = cffi.dlopen(os.path.abspath("simple.dll"))

print(simple.sqrt(2))
Enter fullscreen mode Exit fullscreen mode

Note that cdef uses C declarations, which is annoying but mostly a mechanical translation from Zig.

And there's more! We can build anything into the library and access the full power of Zig. Allocation, io, debug symbols all work.

For more examples including string/struct/dict translation, I created a repository that demonstrates all of that.

https://github.com/Pyrolistical/zig-cffi-python

Top comments (1)

Collapse
 
guidorice profile image
guidorice

Nice post! I just confirmed this works on macos-arm64 as well. Couple of additional notes:

If you want to use zig's build system (e.g. project created with zig init-lib), then just change line 18 of build.zig to be a shared lib:

// const lib = b.addStaticLibrary(.{
const lib = b.addSharedLibrary(.{
Enter fullscreen mode Exit fullscreen mode

Then on the python side the file extension would be in a subfolder (again this is on macos).

simple = cffi.dlopen(os.path.abspath("zig-out/lib/projectname.dylib"))
Enter fullscreen mode Exit fullscreen mode