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.
| Name | Kind | Summary |
|---|---|---|
ffi.Callback | class | A Zuri function behind a C function pointer, made by ffi.callback(). |
ffi.CallbackError | class | A callback could not be created, or was used after its release. |
ffi.DeclarationError | class | C or Rust source handed to a declaration could not be read. |
ffi.Declarations | class | A set of declarations: types, functions, variables and constants, read from any mix of C and Rust source. |
ffi.EnumType | class | A C enum, or a Rust enum without fields. |
ffi.FfiError | class | Base class for every error this module raises. |
ffi.LIBC | constant | The C runtime library of this platform, as open() takes it: libc.so.6 on Linux, the system library on… |
ffi.LIBM | constant | The C maths library, which on macOS and Windows is the same library as LIBC. |
ffi.Library | class | A shared library loaded into the process, or the process itself. |
ffi.LinkError | class | A static library could not be linked into a loadable one. |
ffi.LoadError | class | A library could not be found or loaded. |
ffi.Pointer | class | An address in native memory. |
ffi.PointerError | class | Memory was touched in a way that would have been undefined in C. |
ffi.RecordType | class | A struct or a union, built member by member. |
ffi.StructType | class | A C struct, or a Rust #[repr(C)] struct or enum with fields. |
ffi.SymbolError | class | A library does not export a symbol that was asked for. |
ffi.Type | class | A C type. |
ffi.Typed | class | A value marked with the C type it should travel as. |
ffi.UnionType | class | A C union. |
ffi.alloc | function | Zeroed memory for count values of type, typed as type. |
ffi.alloc_bytes | function | size zeroed bytes, as an untyped pointer. |
ffi.alloc_string | function | A NUL-terminated copy of text in memory that lasts, typed as its code unit: char, char16_t, wchar_t… |
ffi.array | function | An array type. |
ffi.at | function | A pointer to a known address. |
ffi.bool | constant | bool: C’s _Bool and Rust’s bool, one byte; a Zuri bool. |
ffi.callback | function | A lasting C function pointer that calls function. |
ffi.char | constant | char: signed on x86-64 and on Apple’s Arm platforms, unsigned on Arm Linux, as the platform’s C compiler… |
ffi.char16_t | constant | char16_t, a UTF-16 code unit. |
ffi.char32_t | constant | char32_t, a UTF-32 code unit. |
ffi.complex_double | constant | double _Complex, as complex_float with doubles. |
ffi.complex_float | constant | float _Complex: a list of two numbers, [real, imaginary]. |
ffi.declarations | function | An empty set of declarations to add C and Rust source to. |
ffi.declare | function | A set of declarations read from C source; the same as declarations().declare(source). |
ffi.declare_rust | function | A set of declarations read from Rust source; the same as declarations().declare_rust(source). |
ffi.default_link_cache | function | The directory link() keeps linked libraries in by default: $XDG_CACHE_HOME/zuri/ffi (or… |
ffi.describe | function | What a foreign function is: { name, pointer, type, threaded, library }, where pointer is its address as a… |
ffi.double | constant | double, 64 bits. |
ffi.enum | function | A new enum type to add constants to. |
ffi.errno | function | errno as the most recent foreign call on this isolate left it. |
ffi.f32 | constant | Rust’s f32. |
ffi.f64 | constant | Rust’s f64. |
ffi.find | function | Where name would be loaded from, without loading it, or nil when it cannot be found. |
ffi.float | constant | float, 32 bits. |
ffi.function | function | A Zuri function that calls the C function at pointer with the signature of type. |
ffi.function_type | function | A function type, for callbacks and for calling function pointers. |
ffi.i128 | constant | Rust’s i128. |
ffi.i16 | constant | Rust’s i16. |
ffi.i32 | constant | Rust’s i32. |
ffi.i64 | constant | Rust’s i64. |
ffi.i8 | constant | Rust’s i8. |
ffi.int | constant | int, 32 bits. |
ffi.int128 | constant | __int128, a 128-bit integer; Rust’s i128. |
ffi.int16 | constant | int16_t. |
ffi.int32 | constant | int32_t. |
ffi.int64 | constant | int64_t. |
ffi.int8 | constant | int8_t. |
ffi.intptr_t | constant | intptr_t, 64 bits. |
ffi.is_foreign | function | Whether value is a foreign function this module made. |
ffi.isize | constant | Rust’s isize. |
ffi.last_error | function | On Windows, GetLastError() as the most recent foreign call on this isolate left it, cleared before the call… |
ffi.link | function | Links static libraries into a shared one with the platform’s linker, and loads it. |
ffi.long | constant | long: 64 bits, except on Windows, where it is 32. |
ffi.longdouble | constant | long double: 80-bit extended precision on x86-64 Linux and macOS, 128-bit quad precision on Arm Linux, and… |
ffi.longlong | constant | long long, 64 bits. |
ffi.malloc | function | size zeroed bytes from the C allocator, as an untyped pointer. |
ffi.open | function | Loads a shared library. |
ffi.platform | function | The facts about this platform that decide how C types are laid out: `{ os, arch, pointer_size, long_size,… |
ffi.pointer | function | A pointer type. |
ffi.ptr | constant | void *, the untyped pointer. |
ffi.ptrdiff_t | constant | ptrdiff_t, 64 bits. |
ffi.rust_char | constant | Rust’s char: four bytes holding a Unicode scalar value, which is a one-character string on the Zuri side. |
ffi.schar | constant | signed char. |
ffi.serve | function | Runs every callback called from another thread that is waiting for this isolate, returning how many ran. |
ffi.set_errno | function | Sets errno, for the rare API that reads it. |
ffi.short | constant | short, 16 bits. |
ffi.size_t | constant | size_t, 64 bits. |
ffi.slice | function | The #[repr(C)] struct of a pointer and a length that a Rust slice is passed across the C ABI as: `{ ptr,… |
ffi.ssize_t | constant | ssize_t, 64 bits. |
ffi.string | constant | const char * that converts to and from a string: a string passed in becomes a NUL-terminated UTF-8 copy for… |
ffi.struct | function | A new struct type to add members to. |
ffi.threaded | function | The same foreign function, run on a helper thread while the calling isolate keeps answering callbacks called… |
ffi.type | function | A type written as C writes it, using only built-in type names: 'unsigned long', 'const char *',… |
ffi.u128 | constant | Rust’s u128. |
ffi.u16 | constant | Rust’s u16. |
ffi.u32 | constant | Rust’s u32. |
ffi.u64 | constant | Rust’s u64. |
ffi.u8 | constant | Rust’s u8. |
ffi.uchar | constant | unsigned char. |
ffi.uint | constant | unsigned int, 32 bits. |
ffi.uint128 | constant | unsigned __int128; Rust’s u128. |
ffi.uint16 | constant | uint16_t. |
ffi.uint32 | constant | uint32_t. |
ffi.uint64 | constant | uint64_t. |
ffi.uint8 | constant | uint8_t. |
ffi.uintptr_t | constant | uintptr_t, 64 bits. |
ffi.ulong | constant | unsigned long: 64 bits, except on Windows, where it is 32. |
ffi.ulonglong | constant | unsigned long long, 64 bits. |
ffi.union | function | A new union type to add members to. |
ffi.ushort | constant | unsigned short, 16 bits. |
ffi.usize | constant | Rust’s usize. |
ffi.void | constant | void: the return type of a function that returns nothing. |
ffi.wchar_t | constant | wchar_t: 16 bits and unsigned on Windows, 32 bits elsewhere. |
ffi.wstring | constant | const wchar_t * that converts to and from a string, as string does in wchar_t’s encoding: UTF-16 on… |
Submodules
| Module | Reached as | Summary |
|---|---|---|
ffi.callback | ffi.callback.* | Zuri functions that C can call. |
ffi.declare | ffi.declare.* | Declarations read from C and Rust source. |
ffi.errors | ffi.* | Every error the ffi module raises, under one root. |
ffi.library | ffi.library.* | A loaded shared library, and binding what it exports. |
ffi.pointer | ffi.pointer.* | Addresses in native memory, and reading and writing through them. |
ffi.types | ffi.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 (defaultfalse);global, make the library’s symbols visible to libraries loaded after it (defaultfalse).lazyandglobalhave 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 toint, which is what C uses.
Returns EnumType
pointer()
ffi.pointer(type, options) -> Type
A pointer type.
Parameters
type(Type) — What it points at;voidfor an untyped pointer.options(dict|nil) —nonnull,trueto refusenilwhere one is passed, as a Rust reference does (defaultfalse);text, an encoding ('utf-8','utf-16','utf-32'or'wide') that makes the pointer convert to and from a string the wayffi.stringdoes.
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(defaultfalse) andabi, as forLibrary.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*constone.
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 whenfunctionraises. Defaults to zero, or nothing for avoidcallback.
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
link()
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 (defaultdefault_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.
default_link_cache()
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