The boundary contract says how values cross between Clojure and Zig.
It defines:
It does not define how Zig computes internally.
Built-in Zig types use exact Zig names as Clojure keywords.
:i8 :i16 :i32 :i64 :i128
:u8 :u16 :u32 :u64 :u128
:isize :usize
:f16 :f32 :f64 :f80 :f128
:bool
:void
:noreturn
This avoids ambiguity with JVM/Clojure names such as long, double, boolean, or nil.
Compound boundary types are vectors.
[:ptr T]
[:ptr :const T]
[:manyptr T]
[:manyptr :const T]
[:slice T]
[:slice :const T]
[:array n T]
[:optional T]
[:error-union E T]
[:owned T]
[:borrowed T]
[:handle T]
Examples:
[:slice :const :u8]
[:slice :f64]
[:ptr :const :i64]
[:array 4 :f32]
[:owned [:slice :u8]]
[:handle Parser]
The core contract should remain plain data. Helper functions may build these forms, but the forms themselves should not be opaque objects.
Destructured arguments pair a map binding with a field-map type, such as {:x :f64 :y :f64}. The field-map describes Clojure-side fields and lowers to native scalars before the call. It is a Clojure-side input shape, not a Zig boundary type. See Interface Design for the destructuring form.
Mutability is explicit.
[:slice :const :u8]
means Zig receives a read-only slice.
[:slice :u8]
means Zig receives a mutable slice and may mutate elements during the call.
Proof-of-concept lifetime rules should be conservative:
:void returns nil;The boundary contract belongs at the edge. Inside that edge, Zig is free.
Zig implementations may use:
The Clojure side should not try to model these internals.
Scalar returns are direct:
:i64 ;; Clojure integer, usually Long when in range
:f64 ;; Double
:bool ;; Boolean
:void ;; nil
Unsigned returns follow a fixed policy: a value within the signed JVM
range comes back as a Long; a :u64 or :usize value beyond it
becomes a BigInteger, never truncated to a negative.
Composite returns need layout and ownership metadata.
:ret Point
may produce a Clojure record if Point was declared with defrecordz.
Owned returns are explicit:
:ret [:owned [:slice :u8]]
They must define who frees memory and whether Clojure copies or wraps it.
The public signature:
[xs [:slice :const :f64]
:ret :f64]
should normalize to data such as:
{:args [{:binding 'xs
:type [:slice :const :f64]}]
:ret :f64}
Type forms should also normalize:
[:slice :const :u8]
to:
{:kind :slice
:const? true
:of {:kind :scalar :name :u8}}
These normalized forms should be inspectable and reusable by advanced users.
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |