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.types

import ffi.types

ffi lifts part of this module out to its own top level; each name below is shown with the path that reaches it. Anything still spelled ffi.types.* needs import ffi.types.

C types as values.

Every type the module works with is a Type: the built-in ones the module exports (ffi.int, ffi.double, ffi.size_t), the ones built from them (ffi.pointer(ffi.char), ffi.array(ffi.int, 4)), records built member by member, and every type a declaration names. A type knows its size and alignment on this platform, and it is what every conversion between a Zuri value and C memory is driven by.

Records are built with chained calls and are laid out the first time anything needs their size, after which they can no longer change:

var Point = ffi.struct('Point')
  .add_field('x', ffi.double)
  .add_field('y', ffi.double)

echo Point.size()

Classes

Type

class ffi.Type

A C type.

Types are never constructed directly. They come from the module’s constants and builders, and from declarations.

  • printable — has a @to_string(), so echo and print() show something useful

Constructor

ffi.Type()

Type.name()

ffi.Type.name() -> string

The type as C spells it: int, const char *, struct point, or the name a typedef or a Rust declaration gave it.

Returns string

Type.kind()

ffi.Type.kind() -> string

What sort of type this is.

One of 'void', 'bool', 'int' (every integer and character type), 'float' (float, double and long double), 'complex', 'char' (a Rust char), 'pointer', 'string' (a pointer that converts to and from a string), 'function pointer', 'array', 'struct', 'union', 'enum' (C enums and Rust enums alike) and 'function'.

Returns string

Type.size()

ffi.Type.size() -> number

Size in bytes on this platform, as C’s sizeof gives it.

Asking for a record’s size lays the record out, after which no member can be added to it.

Returns number

Raises FfiError when the type has no size: void, a function, an array without a length, or a record that was declared and never defined.

Type.align()

ffi.Type.align() -> number

Alignment in bytes on this platform, as C’s _Alignof gives it.

Returns number

Raises FfiError when the type has no alignment, for the same types that have no size.

Type.is_const()

ffi.Type.is_const() -> bool

Whether the type is const-qualified.

A pointer to a const type is never written through, so a list or dictionary passed for one is not updated after the call.

Returns bool

Type.target()

ffi.Type.target() -> Type|nil

The type this one is made from: what a pointer points at, what an array holds, or the integer type an enum is stored as. nil for every other type.

Returns Type|nil

Type.length()

ffi.Type.length() -> number|nil

An array type’s number of elements, or nil for any other type and for an array declared without a length.

Returns number|nil

Type.signature()

ffi.Type.signature() -> dict|nil

The signature of a function type or a function pointer type, as { returns, params, variadic, abi }, or nil for any other type.

returns is a Type, params a list of them, variadic whether arguments may follow the fixed ones, and abi the calling convention’s name: 'default', 'win64' or 'sysv64'.

Returns dict|nil

Type.pointer()

ffi.Type.pointer() -> Type

A pointer to this type; the same as ffi.pointer(type).

Returns Type

Type.array()

ffi.Type.array(length) -> Type

An array of length values of this type; the same as ffi.array(type, length).

Parameters

  • length (number|nil) — Leave out for an array of unknown length, as a flexible array member is declared.

Returns Type

Type.as_const()

ffi.Type.as_const() -> Type

This type with const added.

Returns Type

Type.of()

ffi.Type.of(value) -> Typed

value marked as this type, for passing to a variadic function.

A variadic function’s extra arguments have no declared types, so each one otherwise travels as the type its value suggests (see Library.function()). This fixes it instead:

c.printf('%f %ld\n', ffi.double.of(1), ffi.long.of(7))

C’s default promotions still apply on top: a float travels as a double, and an integer narrower than int as an int.

Parameters

  • value (any)

Returns Typed

Type.equals()

ffi.Type.equals(other) -> bool

Whether other is the same type.

Scalars, pointers and arrays compare by shape, so ffi.int32 equals ffi.int, and a typedef equals what it names. Records and enums compare by identity: two structs built separately are different types even with the same members, as they are in C.

Parameters

  • other (any)

Returns bool

Type.to_string()

ffi.Type.to_string()

RecordType

class ffi.RecordType < Type

A struct or a union, built member by member.

Members go in declaration order, each at the offset the platform’s C compiler would give it: GCC and Clang’s rules on Linux and macOS, MSVC’s on Windows. The two differ only for bitfields. The record is laid out the first time anything needs its size, including passing a value of it, and from then on it cannot change.

  • printable — has a @to_string(), so echo and print() show something useful

RecordType.add_field()

ffi.RecordType.add_field(name: string, type, align)

Adds a member, returning the record for chaining.

var Header = ffi.struct('Header')
  .add_field('magic', ffi.uint32)
  .add_field('payload', ffi.uint8.array(16))
  .add_field('checksum', ffi.uint64, 16)

An array type without a length is a flexible array member and must come last. A struct or union added with an empty name is an anonymous member, whose own members are reached as this record’s.

Parameters

  • name (string)
  • type (Type)
  • align (number|nil) — A minimum alignment in bytes, as _Alignas gives a member. A power of two. Defaults to the type’s own.

Returns — self

Raises FfiError when the record has already been laid out, the name is taken, or a member follows a flexible array member.

RecordType.add_bitfield()

ffi.RecordType.add_bitfield(name: string, type, bits: number)

Adds a bitfield, returning the record for chaining.

var Flags = ffi.struct('Flags')
  .add_bitfield('ready', ffi.uint, 1)
  .add_bitfield('mode', ffi.uint, 3)
  .add_bitfield('', ffi.uint, 0)
  .add_bitfield('count', ffi.uint, 12)

An empty name is an unnamed bitfield, which only takes up space; one of width zero ends the current storage unit, as in C.

Parameters

  • name (string)
  • type (Type) — An integer, bool or enum type.
  • bits (number) — The width, at most the type’s own.

Returns — self

Raises FfiError when the type is not an integer type or the width does not fit it.

RecordType.set_packed()

ffi.RecordType.set_packed(bytes)

Packs the members to at most bytes alignment, as #pragma pack(n) does, or tightly when bytes is left out, as __attribute__((packed)) does.

Parameters

  • bytes (number|nil) — 1, 2, 4, 8 or 16. Defaults to 1.

Returns — self

RecordType.set_align()

ffi.RecordType.set_align(bytes: number)

Raises the record’s own alignment to at least bytes, as __attribute__((aligned(n))) on the record does. Its size grows to a multiple of the new alignment.

Parameters

  • bytes (number) — A power of two.

Returns — self

RecordType.fields()

ffi.RecordType.fields() -> list

Every member in declaration order, with the members of anonymous members listed in their place.

Each is { name, type, offset, bits, bit_offset }. offset is in bytes from the start of the record. For a bitfield, bits is its width and bit_offset the position of its first bit from the start of the record; both are nil for every other member.

Returns list

RecordType.offset_of()

ffi.RecordType.offset_of(name: string) -> number

A member’s offset in bytes, as C’s offsetof gives it. For a bitfield, the byte its first bit is in.

Parameters

  • name (string)

Returns number

Raises ValueError when there is no such member.

RecordType.has_field()

ffi.RecordType.has_field(name: string) -> bool

Whether the record has a member called name.

Parameters

  • name (string)

Returns bool

RecordType.variants()

ffi.RecordType.variants() -> dict|nil

For a Rust enum with fields, each variant’s name and discriminant; nil for an ordinary record.

Returns dict|nil

StructType

class ffi.StructType < RecordType

A C struct, or a Rust #[repr(C)] struct or enum with fields.

A struct value passes into C as a dictionary of its members and comes back out as one. A member left out of the dictionary is zero. A Rust enum with fields is a dictionary too, with a variant key naming the variant, and a variant without fields may be passed as just its name.

  • printable — has a @to_string(), so echo and print() show something useful

UnionType

class ffi.UnionType < RecordType

A C union.

A union value passes into C as a dictionary holding exactly one of its members, the one to store. Read back, it is a dictionary of every member, each read from the same bytes, since which one is meaningful is something only the program knows.

  • printable — has a @to_string(), so echo and print() show something useful

EnumType

class ffi.EnumType < Type

A C enum, or a Rust enum without fields.

A value of an enum type is a number, and anywhere one is passed in, the name of a constant can be passed instead:

var Color = ffi.enum('Color').add_constant('RED', 0).add_constant('GREEN', 1)
set_color('GREEN')
  • printable — has a @to_string(), so echo and print() show something useful

EnumType.add_constant()

ffi.EnumType.add_constant(name: string, value)

Adds a named constant, returning the enum for chaining.

Parameters

  • name (string)
  • value (number) — An integer that fits the enum’s storage type.

Returns — self

Raises ValueError when the enum already has a constant of that name.

Raises RangeError when the value does not fit.

EnumType.constants()

ffi.EnumType.constants() -> dict

Every constant, by name, in the order they were declared.

Returns dict

EnumType.value()

ffi.EnumType.value(name: string) -> number

The value of the constant called name.

Parameters

  • name (string)

Returns number

Raises ValueError when there is no such constant.

Typed

class ffi.Typed

A value marked with the C type it should travel as. Made by Type.of(), and only meaningful as an argument to a variadic function.

  • printable — has a @to_string(), so echo and print() show something useful

Constructor

ffi.Typed(type, value)

Parameters

  • type (Type)
  • value (any)

Typed.type()

ffi.Typed.type() -> Type

The type the value travels as.

Returns Type

Typed.value()

ffi.Typed.value() -> any

The value itself.

Returns any

Typed.to_string()

ffi.Typed.to_string()

2026, Richard Ore and Zuri contributors