Skip to content

3. FFI and native interop

Start from the native declaration, not from a guessed Flecha type. Record target ABI, calling convention, exact integer widths, pointer constness, structure layout, string representation, lifetime, thread rules, and error reporting. Reject a platform at build time when those facts do not match.

Keep extern declarations private to one interop module. That module validates safe inputs, performs the smallest raw conversion, calls native code, copies or adopts results according to the documented owner, and converts failure to a Flecha error. Callers should not need unsafe merely because the implementation uses FFI.

Embedded native sources are part of the package and build contract. Pin their compiler flags, qualify each target, and test nulls, empty buffers, maximum lengths, callbacks, repeated calls, and cleanup after native failure.

Tested example

Embed one C translation unit, declare its exact scalar ABI, and return to safe Flecha immediately.

File: curriculum/main-evidence/single/foreign_add.fl

curriculum/main-evidence/single/foreign_add.fl
foreign c {
#include <stdint.h>
int32_t native_add(int32_t left, int32_t right) {
return left + right;
}
}
extern i32 native_add(i32 left, i32 right);
i32 main() {
stdoutln(native_add(20, 22));
return 0;
}

The block above is the complete file executed by the commands below.

The external declaration matches one scalar C function and returns immediately to typed Flecha. This example does not cover structures, callbacks, ownership transfer, or target-specific ABI differences.

Commands

Commands
flecha check curriculum/main-evidence/single/foreign_add.fl
flecha build curriculum/main-evidence/single/foreign_add.fl -o build/foreign_add
build/foreign_add

Expected output

Expected output
42
exit status: 0

Related reference: /docs/reference/extern/.