Rust

FFI — Integrando Rust com C e Outras Linguagens Já leu

17 min de leitura

FFI — Integrando Rust com C e Outras Linguagens
O mundo real raramente é monolítico. Sistemas legados em C, bibliotecas de alto desempenho em C++, scripts Python, aplicações Java — na prática, software de produção é uma colagem de linguagens e tecnologias. A FFI (Fore

 

O mundo real raramente é monolítico. Sistemas legados em C, bibliotecas de alto desempenho em C++, scripts Python, aplicações Java — na prática, software de produção é uma colagem de linguagens e tecnologias. A FFI (Foreign Function Interface) é o mecanismo que permite que Rust converse com esse ecossistema.

Neste artigo cobrimos os dois sentidos da integração: chamar código C a partir de Rust, e expor Rust para ser chamado por C, Python, Java e outras linguagens. É território que exige atenção — a fronteira entre linguagens é onde as garantias do compilador terminam e o unsafe começa.


Chamando C a partir de Rust

O básico: extern "C"

// Declara funções C que queremos chamar
extern "C" {
    // Da libc padrão
    fn strlen(s: *const libc::c_char) -> libc::size_t;
    fn malloc(size: libc::size_t) -> *mut libc::c_void;
    fn free(ptr: *mut libc::c_void);
    fn memcpy(
        dest: *mut libc::c_void,
        src: *const libc::c_void,
        n: libc::size_t,
    ) -> *mut libc::c_void;
    fn printf(format: *const libc::c_char, ...) -> libc::c_int;
    fn abs(x: libc::c_int) -> libc::c_int;
}

use std::ffi::CString;

fn demonstrar_ffi_basico() {
    // strlen
    let s = CString::new("Olá, mundo!").unwrap();
    let comprimento = unsafe { strlen(s.as_ptr()) };
    println!("strlen: {comprimento}");

    // abs
    let n: libc::c_int = -42;
    let resultado = unsafe { abs(n) };
    println!("abs(-42): {resultado}");
}

fn main() {
    demonstrar_ffi_basico();
}

Adicione ao Cargo.toml:

[dependencies]
libc = "0.2"

Strings entre Rust e C

A fronteira de strings é a fonte mais comum de bugs em FFI:

use std::ffi::{CStr, CString, NulError};
use std::os::raw::c_char;

// Rust → C: CString garante terminação nula
fn rust_para_c(s: &str) -> Result<CString, NulError> {
    // Erro se a string contém bytes nulos internos
    CString::new(s)
}

// C → Rust: CStr para leitura sem cópia
unsafe fn c_para_rust_borrow(ptr: *const c_char) -> &'static str {
    // SAFETY: ptr deve ser não-nulo e válido por toda a vida útil
    CStr::from_ptr(ptr)
        .to_str()
        .expect("String C não é UTF-8 válida")
}

// C → Rust: String alocada (mais seguro para ownership)
unsafe fn c_para_rust_owned(ptr: *const c_char) -> String {
    CStr::from_ptr(ptr)
        .to_string_lossy()
        .into_owned()
}

// Exemplo prático: envolver função C que retorna string
extern "C" {
    fn strerror(errnum: libc::c_int) -> *const c_char;
}

fn mensagem_erro_sistema(codigo: i32) -> String {
    unsafe {
        let ptr = strerror(codigo as libc::c_int);
        if ptr.is_null() {
            return format!("Erro desconhecido: {codigo}");
        }
        CStr::from_ptr(ptr).to_string_lossy().into_owned()
    }
}

fn main() {
    // Erros POSIX comuns
    for codigo in [0, 1, 2, 13, 22] {
        println!("errno {codigo:2}: {}", mensagem_erro_sistema(codigo));
    }

    // Convertendo strings
    let s_rust = "texto em Rust";
    let s_c = CString::new(s_rust).unwrap();
    println!("CString: {:?}", s_c);
    println!("Bytes incluindo nul: {}", s_c.as_bytes_with_nul().len());
}

Vinculando a uma biblioteca C

Criando e vinculando a uma lib C customizada

src/matematica.c:

#include <math.h>
#include <stdlib.h>

// Função simples
double raiz_quadrada_segura(double x) {
    if (x < 0.0) return -1.0;
    return sqrt(x);
}

// Struct que será usada em Rust
typedef struct {
    double x;
    double y;
    double z;
} Vetor3D;

Vetor3D vetor_normalizar(Vetor3D v) {
    double mag = sqrt(v.x*v.x + v.y*v.y + v.z*v.z);
    if (mag == 0.0) return v;
    Vetor3D resultado = {v.x/mag, v.y/mag, v.z/mag};
    return resultado;
}

double vetor_magnitude(Vetor3D v) {
    return sqrt(v.x*v.x + v.y*v.y + v.z*v.z);
}

// Callback — Rust vai passar função para C chamar
typedef int (*FuncaoFiltro)(int valor);

int filtrar_array(
    const int* entrada,
    int* saida,
    int tamanho,
    FuncaoFiltro filtro
) {
    int count = 0;
    for (int i = 0; i < tamanho; i++) {
        if (filtro(entrada[i])) {
            saida[count++] = entrada[i];
        }
    }
    return count;
}

build.rs — script de build para compilar e vincular:

fn main() {
    // Compila a biblioteca C
    cc::Build::new()
        .file("src/matematica.c")
        .compile("matematica");

    // Vincula com libm (para sqrt)
    println!("cargo:rustc-link-lib=m");

    // Recompila se o arquivo C mudar
    println!("cargo:rerun-if-changed=src/matematica.c");
}

Adicione ao Cargo.toml:

[build-dependencies]
cc = "1"

src/main.rs:

use std::os::raw::c_int;

// Struct correspondente ao Vetor3D em C
// repr(C) garante o mesmo layout de memória
#[repr(C)]
#[derive(Debug, Clone, Copy)]
struct Vetor3D {
    x: f64,
    y: f64,
    z: f64,
}

impl Vetor3D {
    fn novo(x: f64, y: f64, z: f64) -> Self {
        Vetor3D { x, y, z }
    }
}

// Tipo para callback C
type FuncaoFiltro = extern "C" fn(c_int) -> c_int;

extern "C" {
    fn raiz_quadrada_segura(x: f64) -> f64;
    fn vetor_normalizar(v: Vetor3D) -> Vetor3D;
    fn vetor_magnitude(v: Vetor3D) -> f64;
    fn filtrar_array(
        entrada: *const c_int,
        saida: *mut c_int,
        tamanho: c_int,
        filtro: FuncaoFiltro,
    ) -> c_int;
}

// Callback em Rust para passar ao C
// extern "C" garante a ABI correta
extern "C" fn e_par(valor: c_int) -> c_int {
    (valor % 2 == 0) as c_int
}

extern "C" fn e_positivo(valor: c_int) -> c_int {
    (valor > 0) as c_int
}

fn main() {
    // Funções matemáticas
    for x in [-1.0, 0.0, 2.0, 9.0, 16.0] {
        let raiz = unsafe { raiz_quadrada_segura(x) };
        println!("sqrt({x:5.1}) = {raiz:.4}");
    }

    println!();

    // Operações com vetores
    let v = Vetor3D::novo(3.0, 4.0, 0.0);
    let mag = unsafe { vetor_magnitude(v) };
    let norm = unsafe { vetor_normalizar(v) };

    println!("Vetor    : {:?}", v);
    println!("Magnitude: {mag:.4}");
    println!("Normaliz.: {:?}", norm);
    println!("Mag norm : {:.4}", unsafe { vetor_magnitude(norm) });

    println!();

    // Callbacks: passando função Rust para C
    let dados: Vec<c_int> = (-5..=10).collect();
    let mut saida = vec![0c_int; dados.len()];

    let n_pares = unsafe {
        filtrar_array(
            dados.as_ptr(),
            saida.as_mut_ptr(),
            dados.len() as c_int,
            e_par,
        )
    };

    let pares = &saida[..n_pares as usize];
    println!("Números pares em {:?}:", dados);
    println!("  {:?}", pares);

    let n_positivos = unsafe {
        filtrar_array(
            dados.as_ptr(),
            saida.as_mut_ptr(),
            dados.len() as c_int,
            e_positivo,
        )
    };

    println!("Positivos: {:?}", &saida[..n_positivos as usize]);
}

bindgen — gerando bindings automaticamente

Para bibliotecas C grandes, escrever bindings manualmente é impraticável. bindgen lê headers C e gera o código Rust automaticamente:

[build-dependencies]
bindgen = "0.69"
cc = "1"

build.rs:

fn main() {
    // Compila a biblioteca
    cc::Build::new()
        .file("src/matematica.c")
        .compile("matematica");

    println!("cargo:rustc-link-lib=m");
    println!("cargo:rerun-if-changed=src/matematica.h");
    println!("cargo:rerun-if-changed=build.rs");

    // Gera bindings automaticamente do header C
    let bindings = bindgen::Builder::default()
        .header("src/matematica.h")
        // Tipos que queremos incluir
        .allowlist_function("raiz_quadrada_segura")
        .allowlist_function("vetor_.*")
        .allowlist_function("filtrar_array")
        .allowlist_type("Vetor3D")
        // Deriva traits úteis
        .derive_debug(true)
        .derive_copy(true)
        .derive_clone(true)
        .generate()
        .expect("Falha ao gerar bindings");

    let out_dir = std::path::PathBuf::from(
        std::env::var("OUT_DIR").unwrap()
    );
    bindings
        .write_to_file(out_dir.join("bindings.rs"))
        .expect("Falha ao escrever bindings");
}

src/main.rs:

// Inclui os bindings gerados automaticamente
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

fn main() {
    let v = Vetor3D { x: 1.0, y: 2.0, z: 3.0 };
    let mag = unsafe { vetor_magnitude(v) };
    println!("Magnitude: {mag:.4}");
}

Expondo Rust para C

Para que outros programas chamem código Rust, você precisa criar uma biblioteca com ABI C:

Cargo.toml:

[lib]
name = "minha_lib"
crate-type = ["cdylib", "staticlib"]
# cdylib: .so/.dll para vinculação dinâmica
# staticlib: .a/.lib para vinculação estática

src/lib.rs:

use std::ffi::{CStr, CString};
use std::os::raw::{c_char, c_int, c_double};

// Struct exportada para C
#[repr(C)]
pub struct Estatisticas {
    pub media: c_double,
    pub desvio_padrao: c_double,
    pub minimo: c_double,
    pub maximo: c_double,
    pub contagem: c_int,
}

// Funções exportadas com ABI C
// #[no_mangle]: não ofusca o nome da função
// pub extern "C": exporta com ABI C

#[no_mangle]
pub extern "C" fn calcular_estatisticas(
    dados: *const c_double,
    tamanho: c_int,
) -> Estatisticas {
    // Validação de ponteiro nulo
    if dados.is_null() || tamanho <= 0 {
        return Estatisticas {
            media: 0.0,
            desvio_padrao: 0.0,
            minimo: 0.0,
            maximo: 0.0,
            contagem: 0,
        };
    }

    // SAFETY: caller garante que dados aponta para
    // tamanho elementos válidos de f64
    let slice = unsafe {
        std::slice::from_raw_parts(dados, tamanho as usize)
    };

    let n = slice.len() as f64;
    let soma: f64 = slice.iter().sum();
    let media = soma / n;

    let variancia = slice.iter()
        .map(|&x| (x - media).powi(2))
        .sum::<f64>() / n;

    let minimo = slice.iter().cloned().fold(f64::INFINITY, f64::min);
    let maximo = slice.iter().cloned().fold(f64::NEG_INFINITY, f64::max);

    Estatisticas {
        media,
        desvio_padrao: variancia.sqrt(),
        minimo,
        maximo,
        contagem: tamanho,
    }
}

#[no_mangle]
pub extern "C" fn processar_texto(
    entrada: *const c_char,
) -> *mut c_char {
    if entrada.is_null() {
        return std::ptr::null_mut();
    }

    let texto = unsafe {
        match CStr::from_ptr(entrada).to_str() {
            Ok(s) => s,
            Err(_) => return std::ptr::null_mut(),
        }
    };

    // Processa: reverte palavras e coloca em maiúsculas
    let resultado: String = texto
        .split_whitespace()
        .rev()
        .map(str::to_uppercase)
        .collect::<Vec<_>>()
        .join(" ");

    // IMPORTANTE: usa CString::into_raw() para transferir ownership
    // O caller é responsável por chamar liberar_string() depois
    match CString::new(resultado) {
        Ok(s) => s.into_raw(),
        Err(_) => std::ptr::null_mut(),
    }
}

// Função para liberar strings alocadas por Rust
// CRÍTICO: o caller C DEVE chamar esta função
#[no_mangle]
pub extern "C" fn liberar_string(ptr: *mut c_char) {
    if !ptr.is_null() {
        // SAFETY: ptr foi criado por CString::into_raw()
        unsafe { drop(CString::from_raw(ptr)) };
    }
}

// Versão opaca de uma struct — C não precisa conhecer o layout
pub struct ProcessadorTexto {
    separador: String,
    maiusculas: bool,
    contagem: usize,
}

#[no_mangle]
pub extern "C" fn processador_novo(
    separador: *const c_char,
    maiusculas: c_int,
) -> *mut ProcessadorTexto {
    let sep = if separador.is_null() {
        " ".to_string()
    } else {
        unsafe { CStr::from_ptr(separador) }
            .to_string_lossy()
            .into_owned()
    };

    Box::into_raw(Box::new(ProcessadorTexto {
        separador: sep,
        maiusculas: maiusculas != 0,
        contagem: 0,
    }))
}

#[no_mangle]
pub extern "C" fn processador_processar(
    proc: *mut ProcessadorTexto,
    texto: *const c_char,
) -> *mut c_char {
    if proc.is_null() || texto.is_null() {
        return std::ptr::null_mut();
    }

    let processador = unsafe { &mut *proc };
    let texto_str = unsafe {
        match CStr::from_ptr(texto).to_str() {
            Ok(s) => s,
            Err(_) => return std::ptr::null_mut(),
        }
    };

    processador.contagem += 1;

    let partes: Vec<&str> = texto_str.split_whitespace().collect();
    let resultado = if processador.maiusculas {
        partes.iter()
            .map(|s| s.to_uppercase())
            .collect::<Vec<_>>()
            .join(&processador.separador)
    } else {
        partes.join(&processador.separador)
    };

    match CString::new(resultado) {
        Ok(s) => s.into_raw(),
        Err(_) => std::ptr::null_mut(),
    }
}

#[no_mangle]
pub extern "C" fn processador_contagem(
    proc: *const ProcessadorTexto,
) -> c_int {
    if proc.is_null() {
        return -1;
    }
    unsafe { (*proc).contagem as c_int }
}

#[no_mangle]
pub extern "C" fn processador_liberar(proc: *mut ProcessadorTexto) {
    if !proc.is_null() {
        unsafe { drop(Box::from_raw(proc)) };
    }
}

Header C gerado (minha_lib.h):

#ifndef MINHA_LIB_H
#define MINHA_LIB_H

#include <stdint.h>

typedef struct {
    double media;
    double desvio_padrao;
    double minimo;
    double maximo;
    int    contagem;
} Estatisticas;

Estatisticas calcular_estatisticas(const double* dados, int tamanho);

char* processar_texto(const char* entrada);
void  liberar_string(char* ptr);

typedef struct ProcessadorTexto ProcessadorTexto;

ProcessadorTexto* processador_novo(const char* separador, int maiusculas);
char*             processador_processar(ProcessadorTexto* proc, const char* texto);
int               processador_contagem(const ProcessadorTexto* proc);
void              processador_liberar(ProcessadorTexto* proc);

#endif

Usando a biblioteca em C:

#include <stdio.h>
#include <stdlib.h>
#include "minha_lib.h"

int main() {
    // Estatísticas
    double dados[] = {1.0, 2.5, 3.7, 4.1, 5.9, 2.3, 8.1};
    int n = sizeof(dados) / sizeof(dados[0]);

    Estatisticas stats = calcular_estatisticas(dados, n);
    printf("Média: %.4f
", stats.media);
    printf("Desvio: %.4f
", stats.desvio_padrao);
    printf("Min/Max: %.2f / %.2f
", stats.minimo, stats.maximo);

    // Processamento de texto
    char* resultado = processar_texto("hello world from rust");
    if (resultado) {
        printf("Processado: %s
", resultado);
        liberar_string(resultado);  // OBRIGATÓRIO
    }

    // Objeto opaco
    ProcessadorTexto* proc = processador_novo("|", 1);
    char* s1 = processador_processar(proc, "foo bar baz");
    printf("Resultado: %s
", s1);
    liberar_string(s1);

    printf("Chamadas: %d
", processador_contagem(proc));
    processador_liberar(proc);  // OBRIGATÓRIO

    return 0;
}

Integrando com Python via PyO3

PyO3 é a forma idiomática de criar extensões Python em Rust:

[lib]
name = "modulo_rust"
crate-type = ["cdylib"]

[dependencies]
pyo3 = { version = "0.21", features = ["extension-module"] }

src/lib.rs:

use pyo3::prelude::*;
use pyo3::exceptions::PyValueError;

// Função exportada para Python
#[pyfunction]
fn fibonacci(n: u64) -> PyResult<u64> {
    if n > 93 {
        return Err(PyValueError::new_err(
            "n > 93 causaria overflow em u64"
        ));
    }

    if n <= 1 {
        return Ok(n);
    }

    let (mut a, mut b) = (0u64, 1u64);
    for _ in 2..=n {
        (a, b) = (b, a + b);
    }
    Ok(b)
}

#[pyfunction]
fn ordenar_strings(mut lista: Vec<String>) -> Vec<String> {
    lista.sort_by(|a, b| a.to_lowercase().cmp(&b.to_lowercase()));
    lista
}

// Classe exportada para Python
#[pyclass]
struct Contador {
    valor: i64,
    passo: i64,
}

#[pymethods]
impl Contador {
    #[new]
    fn novo(passo: Option<i64>) -> Self {
        Contador {
            valor: 0,
            passo: passo.unwrap_or(1),
        }
    }

    fn incrementar(&mut self) {
        self.valor += self.passo;
    }

    fn decrementar(&mut self) {
        self.valor -= self.passo;
    }

    fn resetar(&mut self) {
        self.valor = 0;
    }

    #[getter]
    fn valor(&self) -> i64 {
        self.valor
    }

    fn __repr__(&self) -> String {
        format!("Contador(valor={}, passo={})", self.valor, self.passo)
    }

    fn __add__(&self, outro: &Contador) -> Contador {
        Contador {
            valor: self.valor + outro.valor,
            passo: self.passo,
        }
    }
}

// Processamento numérico de alta performance
#[pyfunction]
fn calcular_estatisticas(dados: Vec<f64>) -> PyResult<(f64, f64, f64, f64)> {
    if dados.is_empty() {
        return Err(PyValueError::new_err("Lista vazia"));
    }

    let n = dados.len() as f64;
    let soma: f64 = dados.iter().sum();
    let media = soma / n;

    let variancia = dados.iter()
        .map(|&x| (x - media).powi(2))
        .sum::<f64>() / n;

    let min = dados.iter().cloned().fold(f64::INFINITY, f64::min);
    let max = dados.iter().cloned().fold(f64::NEG_INFINITY, f64::max);

    Ok((media, variancia.sqrt(), min, max))
}

// Registra o módulo Python
#[pymodule]
fn modulo_rust(_py: Python, m: &PyModule) -> PyResult<()> {
    m.add_function(wrap_pyfunction!(fibonacci, m)?)?;
    m.add_function(wrap_pyfunction!(ordenar_strings, m)?)?;
    m.add_function(wrap_pyfunction!(calcular_estatisticas, m)?)?;
    m.add_class::<Contador>()?;
    Ok(())
}

Compile e instale:

pip install maturin
maturin develop  # desenvolvimento
maturin build --release  # produção

# Usa em Python:
python3 -c "
import modulo_rust

print(modulo_rust.fibonacci(10))  # 55
print(modulo_rust.ordenar_strings(['Banana', 'apple', 'Cherry']))

c = modulo_rust.Contador(passo=5)
c.incrementar()
c.incrementar()
print(c.valor)  # 10
print(c)        # Contador(valor=10, passo=5)

media, dp, mn, mx = modulo_rust.calcular_estatisticas([1.0, 2.0, 3.0, 4.0, 5.0])
print(f'média={media}, dp={dp:.4f}')
"

Integrando com Java via JNI

[dependencies]
jni = "0.21"

src/lib.rs:

use jni::JNIEnv;
use jni::objects::{JClass, JString, JObject};
use jni::sys::{jint, jlong, jstring, jdouble, jdoubleArray};

// Convenção de nomenclatura JNI:
// Java_<pacote_com_underscores>_<Classe>_<metodo>

#[no_mangle]
pub extern "system" fn Java_com_exemplo_RustLib_fibonacci(
    _env: JNIEnv,
    _class: JClass,
    n: jlong,
) -> jlong {
    if n <= 1 {
        return n;
    }

    let (mut a, mut b) = (0i64, 1i64);
    for _ in 2..=n {
        let c = a.saturating_add(b);
        a = b;
        b = c;
    }
    b
}

#[no_mangle]
pub extern "system" fn Java_com_exemplo_RustLib_processarTexto(
    env: JNIEnv,
    _class: JClass,
    texto: JString,
) -> jstring {
    let texto_rust: String = env
        .get_string(&texto)
        .expect("String Java inválida")
        .into();

    let resultado: String = texto_rust
        .split_whitespace()
        .map(|w| {
            let mut chars = w.chars();
            match chars.next() {
                None => String::new(),
                Some(c) => c.to_uppercase().collect::<String>() + chars.as_str(),
            }
        })
        .collect::<Vec<_>>()
        .join(" ");

    env.new_string(resultado)
        .expect("Falha ao criar string Java")
        .into_raw()
}

#[no_mangle]
pub extern "system" fn Java_com_exemplo_RustLib_calcularMedia(
    env: JNIEnv,
    _class: JClass,
    array: jdoubleArray,
) -> jdouble {
    let dados = unsafe {
        env.get_array_elements(&env.get_double_array_elements(array, jni::objects::ReleaseMode::NoCopyBack).unwrap())
    };

    // Versão simplificada
    let elementos: Vec<f64> = env
        .get_double_array_elements(array, jni::objects::ReleaseMode::NoCopyBack)
        .unwrap()
        .iter()
        .cloned()
        .collect();

    if elementos.is_empty() {
        return 0.0;
    }

    elementos.iter().sum::<f64>() / elementos.len() as f64
}

Uso em Java (RustLib.java):

package com.exemplo;

public class RustLib {
    static {
        System.loadLibrary("minha_lib");
    }

    public static native long fibonacci(long n);
    public static native String processarTexto(String texto);
    public static native double calcularMedia(double[] dados);

    public static void main(String[] args) {
        System.out.println(fibonacci(10));  // 55
        System.out.println(processarTexto("hello world"));  // Hello World
        System.out.println(calcularMedia(new double[]{1.0, 2.0, 3.0}));  // 2.0
    }
}

cbindgen — gerando headers C automaticamente

Assim como bindgen lê headers C e gera Rust, cbindgen faz o inverso:

[build-dependencies]
cbindgen = "0.26"

build.rs:

fn main() {
    let crate_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();

    cbindgen::Builder::new()
        .with_crate(&crate_dir)
        .with_language(cbindgen::Language::C)
        .with_include_guard("MINHA_LIB_H")
        .with_documentation(true)
        .generate()
        .expect("Falha ao gerar header C")
        .write_to_file("include/minha_lib.h");

    println!("cargo:rerun-if-changed=src/lib.rs");
}

Boas práticas de FFI

Nunca retorne referências para dados locais através da fronteira FFI. Os dados precisam sobreviver à chamada — use Box::into_raw para heap-allocated, ou garanta que o lifetime é suficientemente longo.

Toda função que aloca deve ter um par que libera. O padrão _criar/_liberar é a forma idiomática. Documente claramente quem é responsável por liberar cada ponteiro.

Valide todos os ponteiros recebidos de C. C não tem tipos opcionais — null é válido em C e pode chegar para Rust sem aviso. Sempre verifique is_null() antes de desreferenciar.

Use #[repr(C)] em todas as structs que cruzam a fronteira. O layout de memória padrão de Rust não é estável e pode mudar entre versões do compilador.

Documente o contrato de segurança de cada função unsafe. O comentário // SAFETY: deve explicar por que o código é correto — não apenas que é unsafe.

Prefira envelopes seguros a unsafe exposto. A função pública deve ser segura; mova o unsafe para a implementação interna com documentação clara.


Fontes e leituras recomendadas

  • "The Rustonomicon" — FFI chapter — guia oficial sobre FFI unsafe — https://doc.rust-lang.org/nomicon/ffi.html
  • bindgen documentation — geração automática de bindings — https://rust-lang.github.io/rust-bindgen/
  • cbindgen documentation — geração de headers C — https://github.com/mozilla/cbindgen
  • PyO3 User Guide — integração Rust-Python — https://pyo3.rs
  • jni crate documentation — integração com Java — https://docs.rs/jni
  • libc crate — tipos e funções da libc — https://docs.rs/libc
  • "Mixing Rust and C" — Embedded Rust Book — FFI para sistemas embarcados — https://docs.rust-embedded.org/book/interoperability/

 

Comentários

Mais em Rust

Rust em Produção — Observabilidade, Métricas e Operação de Sistemas Rust
Rust em Produção — Observabilidade, Métricas e Operação de Sistemas Rust

Rust — Artigo #48 Rust em Produção — Observabilidade, Métricas e Operação de...

Compiladores e Interpretadores — Construindo uma Linguagem com Rust
Compiladores e Interpretadores — Construindo uma Linguagem com Rust

Rust — Artigo #44 Compiladores e Interpretadores — Construindo uma Linguagem...

Testes — Escrevendo Código que Prova que Seu Código Funciona
Testes — Escrevendo Código que Prova que Seu Código Funciona

&nbsp; Rust tem suporte nativo a testes &mdash; sem frameworks externos, sem...