Skip to content

FFI & External Interface

Use extern declarations to call external symbols such as C library functions or system APIs. When using FFI, make sure your parameter types, memory layout, and resource cleanup match the external API.

extern {
public Int puts(UInt8* text);
public Int open(UInt8* path, Int options);
public Int errno;
}

Single declarations can use top-level modifiers:

extern public Int puts(UInt8* text);
public extern Int errno;

C APIs represent strings as UInt8*. A string literal passed in that context is emitted as null-terminated read-only data:

extern public Int puts(UInt8* text);
puts("hello from Jiang");

Borrowed sentinel string views use UInt8[:0]&. Use $.ptr() when a C API needs a raw pointer. Use view.slice() or view$.as(UInt8[]&) when Jiang code needs to treat a non-literal sentinel view as a plain byte slice:

UInt8[:0]& view = "hello";
unsafe {
puts(view$.ptr());
}
UInt8[]& bytes = view.slice();

When a C API needs a pointer plus a length, model the pointer and length explicitly:

extern public Int write(Int fd, UInt8* buf, Int count);

T* is a raw pointer for FFI, ABI, and low-level code:

Int value = 42;
Int* raw = unsafe {
value$.ptr()
};
Int copied = raw$.get();

Writing through a single-object raw pointer requires a mutable pointee:

Int value! = 0;
Int*! raw = unsafe {
value$.mut_ptr()
};
raw$.set(42);

T* is also the unbounded raw contiguous-memory view used by C buffer parameters:

extern Int read(Int fd, UInt8*! buf, Int count);

Indexing raw pointers is unsafe because they carry no length. T* can be indexed for reads, while T*! also permits writes. Wrap the pointer with a length field or use T[]& when Jiang code needs a borrowed length-bearing view. T[*] and T[*:S] have been removed.

When wrapping external resources, make ownership explicit:

struct CBuffer {
UInt8*! data;
Int length;
deinit(self) {
unsafe {
self.data$.dealloc();
}
return ();
}
}