Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ffi

import ffi

Calling C and Rust libraries, and being called by them.

ffi loads a shared library, describes the functions in it, and calls them with ordinary Zuri values. Every conversion across the boundary is checked against the C type, so a value that does not fit is an error at the call rather than a corrupted argument inside the library. The module reads C and Rust declarations as their headers and crates write them, links static libraries into loadable ones, and turns Zuri functions into C function pointers.

import ffi

var c = ffi.open(ffi.LIBC).declare('
  size_t strlen(const char *s);
  int abs(int n);
')

echo c.strlen('hello')
echo c.abs(-7)

Values at the boundary

Numbers become any C integer or floating-point type, with integers range-checked against their type. Integers too large for a number to hold exactly, 64- and 128-bit ones, travel as bigints both ways. A string passed to a char * becomes a NUL-terminated copy for the duration of the call; bytes pass their own storage with nothing copied; a list or dictionary passed to a pointer becomes a temporary array or struct, written back afterwards unless the pointer is to const. Structs and unions by value are dictionaries. nil is a null pointer, and a null pointer coming back is nil.

Memory

alloc() and its siblings hand out memory that knows its own size, and every access through a pointer into it is bounds-checked and refused once the memory is freed. Memory C returns can be taken over with Pointer.own(), which releases it with the right function when the pointer is collected.

Callbacks

A Zuri function passed to a function-pointer parameter works as a callback for that call. callback() makes one that lasts, for a library that keeps the pointer. A callback can be called from any thread: from another thread, the call waits until the isolate that made the callback runs it.

The ffi API

Every public name in ffi, wherever it is declared. Each links to the page that documents it.

NameKindSummary
ffi.CallbackclassA Zuri function behind a C function pointer, made by ffi.callback().
ffi.CallbackErrorclassA callback could not be created, or was used after its release.
ffi.DeclarationErrorclassC or Rust source handed to a declaration could not be read.
ffi.DeclarationsclassA set of declarations: types, functions, variables and constants, read from any mix of C and Rust source.
ffi.EnumTypeclassA C enum, or a Rust enum without fields.
ffi.FfiErrorclassBase class for every error this module raises.
ffi.LIBCconstantThe C runtime library of this platform, as open() takes it: libc.so.6 on Linux, the system library on…
ffi.LIBMconstantThe C maths library, which on macOS and Windows is the same library as LIBC.
ffi.LibraryclassA shared library loaded into the process, or the process itself.
ffi.LinkErrorclassA static library could not be linked into a loadable one.
ffi.LoadErrorclassA library could not be found or loaded.
ffi.PointerclassAn address in native memory.
ffi.PointerErrorclassMemory was touched in a way that would have been undefined in C.
ffi.RecordTypeclassA struct or a union, built member by member.
ffi.StructTypeclassA C struct, or a Rust #[repr(C)] struct or enum with fields.
ffi.SymbolErrorclassA library does not export a symbol that was asked for.
ffi.TypeclassA C type.
ffi.TypedclassA value marked with the C type it should travel as.
ffi.UnionTypeclassA C union.
ffi.allocfunctionZeroed memory for count values of type, typed as type.
ffi.alloc_bytesfunctionsize zeroed bytes, as an untyped pointer.
ffi.alloc_stringfunctionA NUL-terminated copy of text in memory that lasts, typed as its code unit: char, char16_t, wchar_t…
ffi.arrayfunctionAn array type.
ffi.atfunctionA pointer to a known address.
ffi.boolconstantbool: C’s _Bool and Rust’s bool, one byte; a Zuri bool.
ffi.callbackfunctionA lasting C function pointer that calls function.
ffi.charconstantchar: signed on x86-64 and on Apple’s Arm platforms, unsigned on Arm Linux, as the platform’s C compiler…
ffi.char16_tconstantchar16_t, a UTF-16 code unit.
ffi.char32_tconstantchar32_t, a UTF-32 code unit.
ffi.complex_doubleconstantdouble _Complex, as complex_float with doubles.
ffi.complex_floatconstantfloat _Complex: a list of two numbers, [real, imaginary].
ffi.declarationsfunctionAn empty set of declarations to add C and Rust source to.
ffi.declarefunctionA set of declarations read from C source; the same as declarations().declare(source).
ffi.declare_rustfunctionA set of declarations read from Rust source; the same as declarations().declare_rust(source).
ffi.default_link_cachefunctionThe directory link() keeps linked libraries in by default: $XDG_CACHE_HOME/zuri/ffi (or…
ffi.describefunctionWhat a foreign function is: { name, pointer, type, threaded, library }, where pointer is its address as a…
ffi.doubleconstantdouble, 64 bits.
ffi.enumfunctionA new enum type to add constants to.
ffi.errnofunctionerrno as the most recent foreign call on this isolate left it.
ffi.f32constantRust’s f32.
ffi.f64constantRust’s f64.
ffi.findfunctionWhere name would be loaded from, without loading it, or nil when it cannot be found.
ffi.floatconstantfloat, 32 bits.
ffi.functionfunctionA Zuri function that calls the C function at pointer with the signature of type.
ffi.function_typefunctionA function type, for callbacks and for calling function pointers.
ffi.i128constantRust’s i128.
ffi.i16constantRust’s i16.
ffi.i32constantRust’s i32.
ffi.i64constantRust’s i64.
ffi.i8constantRust’s i8.
ffi.intconstantint, 32 bits.
ffi.int128constant__int128, a 128-bit integer; Rust’s i128.
ffi.int16constantint16_t.
ffi.int32constantint32_t.
ffi.int64constantint64_t.
ffi.int8constantint8_t.
ffi.intptr_tconstantintptr_t, 64 bits.
ffi.is_foreignfunctionWhether value is a foreign function this module made.
ffi.isizeconstantRust’s isize.
ffi.last_errorfunctionOn Windows, GetLastError() as the most recent foreign call on this isolate left it, cleared before the call…
ffi.linkfunctionLinks static libraries into a shared one with the platform’s linker, and loads it.
ffi.longconstantlong: 64 bits, except on Windows, where it is 32.
ffi.longdoubleconstantlong double: 80-bit extended precision on x86-64 Linux and macOS, 128-bit quad precision on Arm Linux, and…
ffi.longlongconstantlong long, 64 bits.
ffi.mallocfunctionsize zeroed bytes from the C allocator, as an untyped pointer.
ffi.openfunctionLoads a shared library.
ffi.platformfunctionThe facts about this platform that decide how C types are laid out: `{ os, arch, pointer_size, long_size,…
ffi.pointerfunctionA pointer type.
ffi.ptrconstantvoid *, the untyped pointer.
ffi.ptrdiff_tconstantptrdiff_t, 64 bits.
ffi.rust_charconstantRust’s char: four bytes holding a Unicode scalar value, which is a one-character string on the Zuri side.
ffi.scharconstantsigned char.
ffi.servefunctionRuns every callback called from another thread that is waiting for this isolate, returning how many ran.
ffi.set_errnofunctionSets errno, for the rare API that reads it.
ffi.shortconstantshort, 16 bits.
ffi.size_tconstantsize_t, 64 bits.
ffi.slicefunctionThe #[repr(C)] struct of a pointer and a length that a Rust slice is passed across the C ABI as: `{ ptr,…
ffi.ssize_tconstantssize_t, 64 bits.
ffi.stringconstantconst char * that converts to and from a string: a string passed in becomes a NUL-terminated UTF-8 copy for…
ffi.structfunctionA new struct type to add members to.
ffi.threadedfunctionThe same foreign function, run on a helper thread while the calling isolate keeps answering callbacks called…
ffi.typefunctionA type written as C writes it, using only built-in type names: 'unsigned long', 'const char *',…
ffi.u128constantRust’s u128.
ffi.u16constantRust’s u16.
ffi.u32constantRust’s u32.
ffi.u64constantRust’s u64.
ffi.u8constantRust’s u8.
ffi.ucharconstantunsigned char.
ffi.uintconstantunsigned int, 32 bits.
ffi.uint128constantunsigned __int128; Rust’s u128.
ffi.uint16constantuint16_t.
ffi.uint32constantuint32_t.
ffi.uint64constantuint64_t.
ffi.uint8constantuint8_t.
ffi.uintptr_tconstantuintptr_t, 64 bits.
ffi.ulongconstantunsigned long: 64 bits, except on Windows, where it is 32.
ffi.ulonglongconstantunsigned long long, 64 bits.
ffi.unionfunctionA new union type to add members to.
ffi.ushortconstantunsigned short, 16 bits.
ffi.usizeconstantRust’s usize.
ffi.voidconstantvoid: the return type of a function that returns nothing.
ffi.wchar_tconstantwchar_t: 16 bits and unsigned on Windows, 32 bits elsewhere.
ffi.wstringconstantconst wchar_t * that converts to and from a string, as string does in wchar_t’s encoding: UTF-16 on…

Submodules

ModuleReached asSummary
ffi.callbackffi.callback.*Zuri functions that C can call.
ffi.declareffi.declare.*Declarations read from C and Rust source.
ffi.errorsffi.*Every error the ffi module raises, under one root.
ffi.libraryffi.library.*A loaded shared library, and binding what it exports.
ffi.pointerffi.pointer.*Addresses in native memory, and reading and writing through them.
ffi.typesffi.types.*C types as values.

Constants

LIBC

ffi.LIBC: string

The C runtime library of this platform, as open() takes it: libc.so.6 on Linux, the system library on macOS, and the Universal C Runtime, ucrtbase.dll, on Windows.

LIBM

ffi.LIBM: string

The C maths library, which on macOS and Windows is the same library as LIBC.

void

ffi.void: Type

void: the return type of a function that returns nothing.

bool

ffi.bool: Type

bool: C’s _Bool and Rust’s bool, one byte; a Zuri bool.

char

ffi.char: Type

char: signed on x86-64 and on Apple’s Arm platforms, unsigned on Arm Linux, as the platform’s C compiler has it.

schar

ffi.schar: Type

signed char.

uchar

ffi.uchar: Type

unsigned char.

short

ffi.short: Type

short, 16 bits.

ushort

ffi.ushort: Type

unsigned short, 16 bits.

int

ffi.int: Type

int, 32 bits.

uint

ffi.uint: Type

unsigned int, 32 bits.

long

ffi.long: Type

long: 64 bits, except on Windows, where it is 32.

ulong

ffi.ulong: Type

unsigned long: 64 bits, except on Windows, where it is 32.

longlong

ffi.longlong: Type

long long, 64 bits.

ulonglong

ffi.ulonglong: Type

unsigned long long, 64 bits.

int8

ffi.int8: Type

int8_t.

uint8

ffi.uint8: Type

uint8_t.

int16

ffi.int16: Type

int16_t.

uint16

ffi.uint16: Type

uint16_t.

int32

ffi.int32: Type

int32_t.

uint32

ffi.uint32: Type

uint32_t.

int64

ffi.int64: Type

int64_t. Values beyond 2^53 in magnitude read back as bigints.

uint64

ffi.uint64: Type

uint64_t. Values above 2^53 read back as bigints.

int128

ffi.int128: Type

__int128, a 128-bit integer; Rust’s i128.

uint128

ffi.uint128: Type

unsigned __int128; Rust’s u128.

size_t

ffi.size_t: Type

size_t, 64 bits.

ssize_t

ffi.ssize_t: Type

ssize_t, 64 bits.

ptrdiff_t

ffi.ptrdiff_t: Type

ptrdiff_t, 64 bits.

intptr_t

ffi.intptr_t: Type

intptr_t, 64 bits.

uintptr_t

ffi.uintptr_t: Type

uintptr_t, 64 bits.

wchar_t

ffi.wchar_t: Type

wchar_t: 16 bits and unsigned on Windows, 32 bits elsewhere.

char16_t

ffi.char16_t: Type

char16_t, a UTF-16 code unit.

char32_t

ffi.char32_t: Type

char32_t, a UTF-32 code unit.

float

ffi.float: Type

float, 32 bits.

double

ffi.double: Type

double, 64 bits.

longdouble

ffi.longdouble: Type

long double: 80-bit extended precision on x86-64 Linux and macOS, 128-bit quad precision on Arm Linux, and the same as double on Windows and Apple’s Arm platforms. A Zuri number is a double, so a wider value is rounded to the nearest double when read.

complex_float

ffi.complex_float: Type

float _Complex: a list of two numbers, [real, imaginary]. Passing one by value is not available on Windows, whose C compiler has no _Complex; in memory it works everywhere.

complex_double

ffi.complex_double: Type

double _Complex, as complex_float with doubles.

ptr

ffi.ptr: Type

void *, the untyped pointer.

string

ffi.string: Type

const char * that converts to and from a string: a string passed in becomes a NUL-terminated UTF-8 copy for the call, and a pointer coming back is read up to its terminator into a string, or is nil when null. Use pointer(char) to receive the pointer itself.

wstring

ffi.wstring: Type

const wchar_t * that converts to and from a string, as string does in wchar_t’s encoding: UTF-16 on Windows and UTF-32 elsewhere.

i8

ffi.i8: Type

Rust’s i8.

u8

ffi.u8: Type

Rust’s u8.

i16

ffi.i16: Type

Rust’s i16.

u16

ffi.u16: Type

Rust’s u16.

i32

ffi.i32: Type

Rust’s i32.

u32

ffi.u32: Type

Rust’s u32.

i64

ffi.i64: Type

Rust’s i64.

u64

ffi.u64: Type

Rust’s u64.

i128

ffi.i128: Type

Rust’s i128.

u128

ffi.u128: Type

Rust’s u128.

isize

ffi.isize: Type

Rust’s isize.

usize

ffi.usize: Type

Rust’s usize.

f32

ffi.f32: Type

Rust’s f32.

f64

ffi.f64: Type

Rust’s f64.

rust_char

ffi.rust_char: Type

Rust’s char: four bytes holding a Unicode scalar value, which is a one-character string on the Zuri side.

Functions

open()

ffi.open(name, options) -> Library

Loads a shared library.

name is a path, a file name, or a bare name the platform’s naming convention completes: sqlite3 is tried as libsqlite3.so and then as the versioned file the loader’s cache lists on Linux, libsqlite3.dylib and sqlite3.framework on macOS, and sqlite3.dll on Windows. The directories in paths are searched first, then the platform’s own search order.

With no name, the result is the running process itself, whose symbols include everything already loaded into it.

var libc = ffi.open(ffi.LIBC)
var local = ffi.open('mylib', { paths: ['./build'] })

Parameters

  • name (string|nil)
  • options (dict|nil) — paths, a list of directories searched first; lazy, resolve symbols as they are first used rather than at load (default false); global, make the library’s symbols visible to libraries loaded after it (default false). lazy and global have no effect on Windows.

Returns Library

Raises LoadError when the library cannot be found or loaded.

find()

ffi.find(name: string, paths) -> string|nil

Where name would be loaded from, without loading it, or nil when it cannot be found. Resolves names the same way open() does.

Parameters

  • name (string)
  • paths (list|nil) — Directories searched first.

Returns string|nil

declarations()

ffi.declarations() -> Declarations

An empty set of declarations to add C and Rust source to.

Returns Declarations

declare()

ffi.declare(source: string) -> Declarations

A set of declarations read from C source; the same as declarations().declare(source).

Parameters

  • source (string)

Returns Declarations

Raises DeclarationError when the source cannot be read.

declare_rust()

ffi.declare_rust(source: string) -> Declarations

A set of declarations read from Rust source; the same as declarations().declare_rust(source).

Parameters

  • source (string)

Returns Declarations

Raises DeclarationError when the source cannot be read.

struct()

ffi.struct(name) -> StructType

A new struct type to add members to.

Parameters

  • name (string|nil) — The name it prints as.

Returns StructType

union()

ffi.union(name) -> UnionType

A new union type to add members to.

Parameters

  • name (string|nil) — The name it prints as.

Returns UnionType

enum()

ffi.enum(name, type) -> EnumType

A new enum type to add constants to.

Parameters

  • name (string|nil) — The name it prints as.
  • type (Type|nil) — The integer type values are stored as. Defaults to int, which is what C uses.

Returns EnumType

pointer()

ffi.pointer(type, options) -> Type

A pointer type.

Parameters

  • type (Type) — What it points at; void for an untyped pointer.
  • options (dict|nil) — nonnull, true to refuse nil where one is passed, as a Rust reference does (default false); text, an encoding ('utf-8', 'utf-16', 'utf-32' or 'wide') that makes the pointer convert to and from a string the way ffi.string does.

Returns Type

array()

ffi.array(type, length) -> Type

An array type.

Parameters

  • type (Type) — The element type.
  • length (number|nil) — Leave out for an array of unknown length.

Returns Type

function_type()

ffi.function_type(returns, params: list, options) -> Type

A function type, for callbacks and for calling function pointers.

var compare = ffi.function_type(ffi.int, [ffi.ptr, ffi.ptr])

Parameters

  • returns (Type)
  • params (list) — The parameter types.
  • options (dict|nil) — variadic (default false) and abi, as for Library.function().

Returns Type

slice()

ffi.slice(type, mutable) -> StructType

The #[repr(C)] struct of a pointer and a length that a Rust slice is passed across the C ABI as: { ptr, len }, with len a usize counting elements.

Parameters

  • type (Type) — The element type.
  • mutable (bool|nil) — Whether the pointer is *mut; defaults to a *const one.

Returns StructType

type()

ffi.type(spelling: string) -> Type

A type written as C writes it, using only built-in type names: 'unsigned long', 'const char *', 'int[4]', 'void (*)(int)'. Declarations.type() resolves names declared there too.

Parameters

  • spelling (string)

Returns Type

Raises DeclarationError when it does not name a type.

alloc()

ffi.alloc(type, count) -> Pointer

Zeroed memory for count values of type, typed as type.

Freed when the last pointer into it is collected, or by Pointer.free().

var numbers = ffi.alloc(ffi.int, 4)
numbers.set(2, 99)

Parameters

  • type (Type)
  • count (number|nil) — Defaults to 1.

Returns Pointer

Raises FfiError when the type has no size.

alloc_bytes()

ffi.alloc_bytes(size, align) -> Pointer

size zeroed bytes, as an untyped pointer.

Parameters

  • size (number)
  • align (number|nil) — A power of two. Defaults to 16, enough for any C type.

Returns Pointer

malloc()

ffi.malloc(size) -> Pointer

size zeroed bytes from the C allocator, as an untyped pointer.

Unlike alloc(), this memory is never freed when the pointer is collected, because the usual reason to want it is to hand it to C code that frees it with free(). Free it with Pointer.free() otherwise.

Parameters

  • size (number)

Returns Pointer

alloc_string()

ffi.alloc_string(text: string, encoding) -> Pointer

A NUL-terminated copy of text in memory that lasts, typed as its code unit: char, char16_t, wchar_t or char32_t.

Needed where a string must outlive a call: stored in a struct, or kept by the library.

Parameters

  • text (string)
  • encoding (string|nil) — 'utf-8' (the default), 'utf-16', 'utf-32' or 'wide'.

Returns Pointer

at()

ffi.at(address, type) -> Pointer

A pointer to a known address. Nothing about the address is checked.

Parameters

  • address (number|bigint)
  • type (Type|nil) — What it points at.

Returns Pointer

function()

ffi.function(pointer, type, name) -> function

A Zuri function that calls the C function at pointer with the signature of type.

Parameters

  • pointer (Pointer)
  • type (Type) — A function type, or a pointer to one.
  • name (string|nil) — The name the function reports.

Returns function

Raises PointerError when the pointer is null.

threaded()

ffi.threaded(function) -> function

The same foreign function, run on a helper thread while the calling isolate keeps answering callbacks called from other threads.

This is for a function that waits on threads of its own which call back into Zuri. Called the ordinary way, such a function would wait forever, because the isolate that has to run the callbacks is the one waiting for it.

Parameters

  • function (function) — A foreign function.

Returns function

Raises TypeError when function is not a foreign function.

is_foreign()

ffi.is_foreign(value) -> bool

Whether value is a foreign function this module made.

Parameters

  • value (any)

Returns bool

describe()

ffi.describe(function) -> dict

What a foreign function is: { name, pointer, type, threaded, library }, where pointer is its address as a Pointer, type its function pointer type, and library the path of the library it came from, or nil.

Parameters

  • function (function) — A foreign function.

Returns dict

callback()

ffi.callback(function, type, options) -> Callback

A lasting C function pointer that calls function.

var on_event = ffi.callback(@(code) {
  echo 'event ${code}'
}, ffi.function_type(ffi.void, [ffi.int]))

lib.set_handler(on_event)

The callback lives until Callback.release(). When the Zuri function raises, C gets error_value (or zero), and the error is raised again as soon as control returns to Zuri.

Parameters

  • function (function)
  • type (Type) — A function type, or a pointer to one.
  • options (dict|nil) — error_value, returned to C when function raises. Defaults to zero, or nothing for a void callback.

Returns Callback

Raises TypeError when type is not a function type or is variadic.

Raises CallbackError when 256 callbacks returning a 128-bit integer under the Microsoft x64 convention are alive already; each one released makes room for another.

serve()

ffi.serve(timeout) -> number

Runs every callback called from another thread that is waiting for this isolate, returning how many ran.

Such calls also run at the isolate’s own safepoints, during foreign calls and in threaded calls, so this is for a program that wants them answered at a point of its choosing, or wants to wait for one.

Parameters

  • timeout (number|nil) — Milliseconds to wait for a call when none is waiting. Returns at once when left out.

Returns number

errno()

ffi.errno() -> number

errno as the most recent foreign call on this isolate left it.

errno is cleared before every foreign call and read immediately after it, so this is always what that one call set, or 0.

Returns number

set_errno()

ffi.set_errno(value)

Sets errno, for the rare API that reads it.

Parameters

  • value (number)

last_error()

ffi.last_error() -> number

On Windows, GetLastError() as the most recent foreign call on this isolate left it, cleared before the call and read after it as errno is. Always 0 on other platforms.

Returns number

ffi.link(archives, options) -> Library

Links static libraries into a shared one with the platform’s linker, and loads it.

var lib = ffi.link('target/release/libgeometry.a')

Every object in the archives is linked in. The result is cached under a name derived from the archives’ contents and the options, so the linker runs once for a given input. An archive holding Rust code is recognised and linked against the system libraries Rust’s standard library needs.

The linker is the C compiler, cc or $CC, on Linux and macOS, and MSVC’s link.exe on Windows, found through the Visual Studio installation. On Windows a static library’s functions are not exported by default, so every symbol the archives define with an unmangled name is.

Parameters

  • archives (string|list) — One path or a list of them.
  • options (dict|nil) — libraries, names to link against (['m']); search_paths, directories to find them in; exports, on Windows the exact symbols to export; linker, the program to run; flags, further arguments for it; cache, the directory results are kept in (default default_link_cache()); rust, whether the archives hold Rust code (detected when left out).

Returns Library

Raises LinkError with the linker’s output when linking fails.

Raises LoadError when the linked library cannot be loaded.

ffi.default_link_cache() -> string

The directory link() keeps linked libraries in by default: $XDG_CACHE_HOME/zuri/ffi (or ~/.cache/zuri/ffi) on Linux, ~/Library/Caches/zuri/ffi on macOS, and %LOCALAPPDATA%\zuri\ffi on Windows.

Returns string

platform()

ffi.platform() -> dict

The facts about this platform that decide how C types are laid out: { os, arch, pointer_size, long_size, wchar_size, char_signed, long_double, complex, layout }.

long_double is 'x87', 'binary128' or 'double'; complex says whether complex numbers can be passed by value; layout is 'msvc' on Windows and 'itanium' elsewhere, the rules records follow.

Returns dict


2026, Richard Ore and Zuri contributors