Recoil logo

Recoil

FFI, the C ABI, and ports

  • Home
  • Getting Started
  • Guide
  • Reference
  • PARSE
  • State Machines
  • FFI & Systems
  • Ports
  • Packages
  • f00 & Embedded
  • Examples
  • Internals

On this page

Loading…

Talking to C

Because Recoil transpiles to C, calling C is direct and declarative. A foreign block names the functions you want; a small ABI layer (c-struct!, c-fn!, c-array!, #extern) models exact C layouts when you need them.

FFI is a trust boundary. Foreign functions run arbitrary C with full privileges. The borrow checker and type system do not make FFI safe — treat foreign dependencies like any other native code and isolate builds and runs appropriately.

foreign

Declare external C functions under a namespace. When the library tag is a header tag like <stdlib.h>, Recoil emits the matching #include automatically.

math: foreign <stdlib.h> [
    abs: "abs" [i32! return: i32!]
]

x: math/abs -7
print x                ; 7

c-string! at the boundary

c-string! is a boundary type for FFI declarations and approved internals — use string! in ordinary code. Ownership metadata for returns is written before the type:

env: foreign <stdlib.h> [
    getenv: "getenv" [c-string! return: c-string!]
]

str: foreign <string.h> [
    dup: "strdup" [c-string! return: [#owned c-string!]]
]

[#owned c-string!] is valid; [c-string! #owned] is rejected.

Opaque pointers

Use :TypeName for opaque C pointers in FFI declarations.

curl: foreign <curl/curl.h> [
    init: "curl_easy_init" [return: :CURL]
    perform: "curl_easy_perform" [:CURL return: i32!]
    cleanup: "curl_easy_cleanup" [:CURL]
]

ABI layouts

For exact C ABI modeling (distinct from ordinary struct!):

c-struct!

point!: make c-struct! [
    x: i32!
    y: i32!
]

c-fn! — callback fields

callbacks!: make c-struct! [
    run: c-fn! [i32! return: i32!]
]

c-array! — fixed inline arrays

name-buffer!: make c-struct! [
    buf: c-array! [char! 16]
]

c-array! is ABI-only and is not the same as vector!.

#extern & typed pointer reads

Declare imported ABI globals with #extern; read them through the normal ABI-aware field/path rules.

#extern vector! __recoil_out_buf

Foreign code can return a raw c-pointer! that you reinterpret as a typed ABI struct pointer for field reads:

raw: make c-pointer! foreign-func
header: as :event-header! raw     ; /field access now resolves through the ABI shape

size-of & exports

Use size-of with ABI-visible types when allocating foreign-visible storage:

bytes: make size! size-of c-pointer!

Recoil can export symbols for C consumers: functions via #export, and globals where the ABI/codegen path supports it. See Building libraries for using the generated header from C.

The unsafe namespace

For direct access to common C standard-library functions without writing a foreign block:

buf: unsafe/malloc 1024
unsafe/memset buf 0 1024
unsafe/free buf

Take the address of a binding with addr-of:

x: 42
p: addr-of x

Ports & I/O

Open handles — files, sockets, TLS, HTTP — are port! values that follow an ownership model: borrow with a get-word to read/write, consume to close.

fp: make port! file/open %data.txt
file/read :fp            ; borrow
file/close fp            ; consume

The layered networking model (tcp, tls, http, https), server reactor, and the ERR_PORT_* taxonomy have their own page — see Ports.

↑

Recoil — an ownership-first, statically-typed language with a static borrow checker.

codeberg.org/rebolek/recoil · boleslav@brezovsky.eu · Apache-2.0