FFI and escape hatches
Glide compiles to C and links against libc, which means the entire C world is one declaration away. Most programs never need this chapter. But when you have to call a system API the stdlib doesn't wrap yet, pull in a C library, or hand-write a hot instruction sequence, Glide gives you a graded set of escape hatches — from "borrow a libc function" all the way down to raw assembly.
Calling a C function: extern fn
extern fn declares a function that the C runtime or libc provides. No body — just the signature, so the typer knows how to call it and the linker resolves the symbol:
extern fn printf(fmt: string, ...) -> i32; // C-style variadic with `...`
extern fn malloc(n: usize) -> *void;
extern fn free(p: *void);
fn main() -> i32 {
printf("hex: %08x\n", 255); // hex: 000000ff
return 0;
}
(malloc, free, calloc, printf are already provided by the prelude — you don't need to re-declare them. They're shown here because they're the canonical example of an extern signature.)
Embedding C: c_raw!
When you need more than a single function — a header, a static helper, platform glue — c_raw! drops verbatim C straight into the generated output. At the top level it's where you #include and define helpers:
c_raw! {
#include <math.h>
static double glide_hypot(double a, double b) {
return sqrt(a * a + b * b);
}
}
extern fn glide_hypot(a: f64, b: f64) -> f64; // expose the helper to Glide
fn main() -> i32 {
println!(glide_hypot(3.0, 4.0)); // 5
return 0;
}
The pattern is two halves: c_raw! defines the C, and an extern fn gives it a Glide-visible signature. Any header you #include before its first use is available to later emitted code.
c_raw! also works inside a function body for a quick one-off — values from the surrounding scope are in scope in the C:
fn flush_stdout() {
c_raw! {
fflush(stdout);
}
}
Per-platform code: @cfg
Systems code often needs to branch on the OS. @cfg("windows") / @cfg("posix") gates a declaration so only the matching target compiles it. Define the same function twice, once per platform:
@cfg("windows") fn os_name() -> string { return "windows"; }
@cfg("posix") fn os_name() -> string { return "posix"; }
fn main() -> i32 {
println!(os_name());
return 0;
}
@cfg also gates a single statement or a { ... } block inside a function, so a function can keep one body and branch only the target-specific parts:
fn describe_os() -> string {
let mut name: string = "unknown";
@cfg("windows") { name = "windows"; }
@cfg("posix") { name = "posix"; }
return name;
}
Each gated piece emits its own #ifdef in the generated C, so the non-matching branch never reaches the C compiler.
Inline assembly: asm!
For the rare hot path where you want exact instructions, asm! maps to GCC's inline-assembly syntax: instruction strings, then optional output, input, and clobber lists separated by :.
fn read_tsc() -> u64 {
let lo: u32 = 0;
let hi: u32 = 0;
asm! volatile { "rdtsc" : "=a"(lo), "=d"(hi) }
return ((hi as u64) << 32) | (lo as u64);
}
The volatile marker tells the compiler not to reorder or eliminate the block even though it looks like its outputs aren't otherwise used. The simplest form is just an instruction: asm! { "nop" }.
The deepest hatch: naked fn
A naked function is emitted with no prologue or epilogue — no automatic stack frame, no register save/restore. Its body must be only asm! blocks, because you're assembling the entire calling convention by hand:
@cfg("posix")
naked fn add_raw(a: i32, b: i32) -> i32 {
asm! { "lea (%rdi,%rsi,1), %eax" }
asm! { "ret" }
}
This is as low as the language goes. You'll almost never need it — it exists for the handful of cases (custom trampolines, syscall stubs, instrumentation) where even a normal asm! block's generated frame is one instruction too many.
The rule for all of these
Recap
Where to next
That's the book — from glide run hello.glide to calling assembly. You've seen the whole language and its library: types, control flow, errors, memory, traits, generics, concurrency, packaging, the stdlib, HTTP services, testing, and FFI.
From here:
- The [Standard library chapters](/book/10-prelude) — every public function, module by module, with examples.
- The [examples](/examples) — larger programs that put the pieces together.
Go build something real with it.