FFI e escape hatches
O Glide compila para C e linka contra a libc, o que significa que o mundo inteiro do C está a uma declaração de distância. A maioria dos programas nunca vai precisar deste capítulo. Mas quando você precisa chamar uma API de sistema que a stdlib ainda não encapsula, puxar uma biblioteca C, ou escrever à mão uma sequência de instruções crítica para desempenho, o Glide oferece um conjunto graduado de escape hatches — de "emprestar uma função da libc" até assembly puro.
Chamando uma função C: extern fn
extern fn declara uma função fornecida pelo runtime C ou pela libc. Sem corpo — apenas a assinatura, para que o verificador de tipos saiba como chamá-la e o linker resolva o símbolo:
extern fn printf(fmt: string, ...) -> i32; // variádico estilo C com `...`
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 já são fornecidos pelo prelude — você não precisa redeclará-los. Eles aparecem aqui por serem o exemplo canônico de uma assinatura extern.)
Embutindo C: c_raw!
Quando você precisa de mais do que uma única função — um header, um helper estático, cola de plataforma — c_raw! despeja C verbatim diretamente na saída gerada. No nível superior, é onde você faz #include e 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; // expõe o helper para o Glide
fn main() -> i32 {
println!(glide_hypot(3.0, 4.0)); // 5
return 0;
}
O padrão tem duas metades: c_raw! define o C, e um extern fn dá a ele uma assinatura visível para o Glide. Qualquer header que você #include antes do primeiro uso fica disponível para o código emitido posteriormente.
c_raw! também funciona dentro do corpo de uma função para algo rápido e pontual — valores do escopo ao redor ficam acessíveis no C:
fn flush_stdout() {
c_raw! {
fflush(stdout);
}
}
Código por plataforma: @cfg
Código de sistemas frequentemente precisa ramificar com base no sistema operacional. @cfg("windows") / @cfg("posix") condiciona uma declaração para que apenas o alvo correspondente a compile. Defina a mesma função duas vezes, uma por plataforma:
@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 também condiciona um único statement ou um bloco { ... } dentro de uma função, de modo que uma função pode manter um único corpo e ramificar apenas as partes específicas do alvo:
fn describe_os() -> string {
let mut name: string = "unknown";
@cfg("windows") { name = "windows"; }
@cfg("posix") { name = "posix"; }
return name;
}
Cada trecho condicional emite seu próprio #ifdef no C gerado, portanto o branch que não corresponde nunca chega ao compilador C.
Assembly inline: asm!
Para o raro caminho crítico onde você quer instruções exatas, asm! mapeia para a sintaxe de assembly inline do GCC: strings de instrução, depois listas opcionais de saída, entrada e clobbers separadas por :.
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);
}
O marcador volatile diz ao compilador para não reordenar nem eliminar o bloco mesmo que suas saídas não pareçam ser usadas de outra forma. A forma mais simples é apenas uma instrução: asm! { "nop" }.
O escape hatch mais profundo: naked fn
Uma função naked é emitida sem prólogo nem epílogo — sem stack frame automático, sem salvar/restaurar registradores. Seu corpo deve conter apenas blocos asm!, porque você está montando a convenção de chamada inteira à mão:
@cfg("posix")
naked fn add_raw(a: i32, b: i32) -> i32 {
asm! { "lea (%rdi,%rsi,1), %eax" }
asm! { "ret" }
}
Este é o nível mais baixo que a linguagem alcança. Você quase nunca vai precisar disso — existe para os poucos casos (trampolins customizados, stubs de syscall, instrumentação) onde até o frame gerado por um bloco asm! normal representa uma instrução a mais.
A regra para todos esses recursos
Recap
O que vem a seguir
Esse é o livro — de glide run hello.glide até chamar assembly. Você viu a linguagem inteira e sua biblioteca: tipos, fluxo de controle, erros, memória, traits, genéricos, concorrência, empacotamento, a stdlib, serviços HTTP, testes e FFI.
A partir daqui:
- Os [capítulos da biblioteca padrão](/book/10-prelude) — cada função pública, módulo por módulo, com exemplos.
- Os [exemplos](/examples) — programas maiores que combinam todas as peças.
Vá construir algo de verdade com isso.