---
title: "Rust FFI: bindgen, cbindgen e Integração com C/C++ em 2026"
url: "https://rustlang.com.br/blog/rust-ffi-bindgen-cbindgen-cpp-2026/"
markdown_url: "https://rustlang.com.br/blog/rust-ffi-bindgen-cbindgen-cpp-2026.MD"
description: "Guia prático de Rust FFI em 2026: quando integrar com C/C++, como usar bindgen e cbindgen, cuidados com unsafe, ABI, ownership, testes e carreira."
date: "2026-06-01"
author: "Equipe Rust Brasil"
---

# Rust FFI: bindgen, cbindgen e Integração com C/C++ em 2026

Guia prático de Rust FFI em 2026: quando integrar com C/C++, como usar bindgen e cbindgen, cuidados com unsafe, ABI, ownership, testes e carreira.


## Por que FFI continua importante para Rust

Rust cresce em backend, WebAssembly, infraestrutura e ferramentas para devs, mas uma parte enorme do software crítico ainda vive em C e C++. Drivers, SDKs de hardware, bibliotecas de criptografia, codecs, bancos de dados, engines, módulos de visão computacional e sistemas industriais não são reescritos de uma vez. Para entrar nesses ambientes sem exigir uma migração total, Rust precisa conversar bem com código existente. É aí que **Rust FFI** vira uma habilidade prática, não apenas um tópico avançado.

FFI significa *Foreign Function Interface*: a fronteira entre Rust e outra linguagem. Na prática, quase sempre essa fronteira usa ABI C, porque C é o denominador comum entre compiladores, sistemas operacionais e runtimes. O Rust chama uma função C, C chama uma função Rust, ou os dois lados compartilham structs, ponteiros e buffers com regras muito explícitas.

Para quem busca [vagas Rust](/vagas/) em sistemas, plataforma, segurança, embarcados ou performance, FFI é um diferencial forte. Muitas empresas não começam com um produto 100% Rust. Elas começam substituindo uma parte insegura, escrevendo um módulo novo, criando uma biblioteca de alta performance ou encapsulando um SDK legado. Saber fazer isso sem transformar `unsafe` em um buraco de manutenção é exatamente o tipo de competência que separa protótipo de produção.

## O modelo mental: fronteira pequena, camada segura

O erro comum é tratar FFI como autorização para espalhar `unsafe` pelo projeto. O caminho saudável é o oposto: mantenha a fronteira pequena, isole o código inseguro e exponha uma API Rust normal para o restante da aplicação. O módulo que conversa com C pode lidar com ponteiros, `repr(C)`, nulidade, tamanho de buffers e códigos de erro. O restante do código deve trabalhar com tipos seguros, `Result`, slices, structs próprias e ownership claro.

Pense em três camadas. A primeira é a assinatura bruta que espelha a API C. A segunda é um wrapper interno que valida argumentos, converte erros e documenta invariantes. A terceira é a API pública Rust que o resto do time usa. Se alguém precisa ler a documentação da biblioteca C para chamar uma função comum do seu crate, a abstração provavelmente está vazando demais.

Esse desenho conversa diretamente com o guia de [unsafe Rust](/artigos/unsafe-rust/). `unsafe` não significa "código perigoso por definição"; significa "o compilador não consegue provar algumas garantias sozinho". Em FFI, você assume responsabilidade por validade de ponteiros, alinhamento, lifetime, thread-safety, ownership e compatibilidade binária. Quanto menor a superfície, menor o risco.

## Chamando C a partir de Rust com bindgen

`bindgen` é a ferramenta mais comum quando você já tem uma biblioteca C e quer gerar bindings Rust a partir de arquivos `.h`. Ele lê headers com Clang e produz declarações Rust compatíveis com structs, enums, constantes e funções. Isso evita escrever manualmente centenas de assinaturas e reduz divergência quando o header muda.

Um fluxo típico começa com um `wrapper.h` pequeno:

```c
#include "vendor_sdk.h"
```

Depois, no `build.rs`, você gera bindings durante o build:

```rust
fn main() {
    println!("cargo:rustc-link-lib=vendor_sdk");
    println!("cargo:rerun-if-changed=wrapper.h");

    let bindings = bindgen::Builder::default()
        .header("wrapper.h")
        .allowlist_function("vendor_.*")
        .allowlist_type("vendor_.*")
        .generate()
        .expect("falha ao gerar bindings");

    bindings
        .write_to_file(std::env::var("OUT_DIR").unwrap() + "/bindings.rs")
        .expect("falha ao escrever bindings");
}
```

No código Rust, a recomendação é incluir os bindings em um módulo privado e escrever wrappers seguros ao redor. Evite reexportar tudo que `bindgen` gerou. Headers C costumam expor detalhes que não deveriam virar API estável do seu crate.

```rust
mod ffi {
    include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}

pub fn versao_do_sdk() -> Result<String, SdkError> {
    let ptr = unsafe { ffi::vendor_version() };
    if ptr.is_null() {
        return Err(SdkError::SemVersao);
    }

    let texto = unsafe { std::ffi::CStr::from_ptr(ptr) };
    Ok(texto.to_string_lossy().into_owned())
}
```

O ponto importante não é decorar a API de `bindgen`. É entender que bindings gerados são uma matéria-prima. A qualidade de produção vem do wrapper, dos testes, da documentação e da disciplina sobre quais tipos atravessam a fronteira.

## Expondo Rust para C/C++ com cbindgen

O caminho inverso acontece quando você quer escrever a parte nova em Rust e permitir que um produto C ou C++ consuma essa biblioteca. Nesse cenário, `cbindgen` gera headers C/C++ a partir de tipos e funções Rust compatíveis. Ele é útil para migração gradual: você entrega uma biblioteca dinâmica ou estática, publica um header e mantém o sistema legado chamando uma API estável.

Uma função exportada precisa usar ABI C e controlar nome de símbolo:

```rust
#[repr(C)]
pub struct ResultadoProcessamento {
    pub codigo: i32,
    pub bytes_processados: usize,
}

#[no_mangle]
pub extern "C" fn processar_buffer(
    entrada: *const u8,
    tamanho: usize,
) -> ResultadoProcessamento {
    if entrada.is_null() {
        return ResultadoProcessamento { codigo: -1, bytes_processados: 0 };
    }

    let dados = unsafe { std::slice::from_raw_parts(entrada, tamanho) };
    ResultadoProcessamento { codigo: 0, bytes_processados: dados.len() }
}
```

Esse exemplo é simples, mas já mostra decisões importantes. A função não recebe `Vec<u8>` nem `String`, porque esses tipos são específicos do Rust. Ela recebe ponteiro e tamanho, uma convenção comum em C. A struct usa `#[repr(C)]` para layout previsível. Erros atravessam a fronteira como código, não como `panic`.

Com `cbindgen`, o header gerado documenta a superfície pública. Isso obriga o time a pensar em estabilidade: quais structs são parte do contrato? Quem aloca memória? Quem libera? O que acontece se a versão C chama uma função de uma biblioteca Rust mais nova? Essas perguntas são menos glamourosas que performance, mas são as que evitam incidentes.

## Ownership, memória e strings: onde bugs nascem

A maior parte dos bugs de FFI aparece em ownership e representação. Quem é dono de um ponteiro? Quem pode liberar a memória? O buffer continua válido depois da chamada? A string é UTF-8, Latin-1 ou bytes arbitrários? O ponteiro pode ser nulo? A função é thread-safe? O callback pode ser chamado depois que o objeto Rust foi destruído?

Uma regra prática: nunca deixe ownership implícito. Se C aloca, C libera. Se Rust aloca para C usar, forneça também uma função Rust explícita para liberar. Se C passa um buffer emprestado, documente que a função Rust não armazena o ponteiro depois de retornar. Se Rust precisa guardar algo entre chamadas, exponha um handle opaco em vez de compartilhar a struct interna.

Strings merecem cuidado especial. `CString` ajuda a enviar texto Rust para C sem bytes nulos internos. `CStr` ajuda a ler strings terminadas em `\0`. Mas isso não resolve encoding automaticamente. Muitas APIs legadas não são UTF-8. Em Windows, algumas APIs usam wide strings. Em SDKs antigos, documentação incompleta pode esconder convenções próprias. Teste com acentos brasileiros de verdade: `ação`, `memória`, `São Paulo`, `configuração`. Isso pega problemas que exemplos ASCII nunca mostram.

## Build, CI e distribuição

FFI também é problema de build. Você pode ter um crate Rust correto e ainda falhar porque o runner de CI não tem Clang, a biblioteca C não está no `LD_LIBRARY_PATH`, o header mudou, o target de cross-compilation não tem toolchain C compatível ou o pacote final não inclui a `.so`, `.dylib` ou `.dll` necessária.

Para produção, trate a integração como parte do produto. Documente dependências nativas. Fixe versões quando possível. Rode CI em Linux e no sistema operacional que seus usuários realmente usam. Se a biblioteca roda em embarcados, teste o target certo. Se o output é um SDK, publique exemplos mínimos em C ou C++ que compilam contra o header gerado.

O conteúdo sobre [release engineering em Rust](/blog/rust-release-engineering-binaries-cli-servicos-2026/) vale muito aqui: checksums, artefatos reproduzíveis, smoke tests e versionamento não são luxo. Em FFI, o consumidor pode nem ser Rust. Um erro de ABI pode aparecer só no runtime de outro produto.

## FFI em embarcados, indústria e migração gradual

No Brasil, FFI aparece com frequência em cenários onde Rust entra como modernização incremental. Uma empresa industrial pode ter firmware, drivers e ferramentas internas em C. Uma fintech pode usar bibliotecas nativas de criptografia ou comunicação com HSM. Uma empresa de dados pode ter um motor C++ antigo que precisa de um módulo novo mais seguro. Um time de produto pode querer expor uma biblioteca Rust para Python, Node ou mobile sem reescrever tudo.

Isso conecta FFI a [Rust para embarcados](/artigos/rust-para-embarcados/), [sistemas embarcados com Embassy](/blog/rust-embedded-embassy-iot-2026/), [Rust vs C](/artigos/rust-vs-c/) e [Rust vs C++](/blog/rust-vs-cpp-2026/). A discussão real raramente é "Rust substitui tudo amanhã". A discussão madura é: qual módulo novo vale escrever em Rust, qual parte legada continua em C/C++, qual fronteira é estável e como provar que a transição reduz risco?

Quem também acompanha linguagens de sistemas pode comparar esse caminho com materiais do <a href="https://ziglang.com.br/" target="_blank" rel="noopener noreferrer" onclick="umami.track('portfolio-site-click', { destination: 'ziglang.com.br' })">Zig Brasil</a>, porque Zig também é forte em interoperabilidade C. A diferença é que Rust força uma camada mais explícita de segurança e ownership, o que pode ser excelente quando o objetivo é conter riscos de memória em módulos novos.

## Projeto de portfólio: wrapper seguro para uma biblioteca C

Um bom projeto público não precisa integrar um SDK secreto. Escolha uma biblioteca C pequena e real, como uma biblioteca de compressão, hash, parser, imagem ou áudio. Gere bindings com `bindgen`, escreva um wrapper Rust seguro, cubra casos de erro e publique exemplos.

O README deve explicar a fronteira: quais funções C são chamadas, quais invariantes o wrapper garante, como memória é alocada, quais tipos são seguros, como rodar testes e como compilar em CI. Inclua também um microbenchmark com [Criterion](/ecossistema/criterion/) se performance for parte da motivação, mas não faça benchmark virar a única história. Segurança e ergonomia importam tanto quanto velocidade.

Para um segundo nível, exponha uma função Rust via `cbindgen` e crie um exemplo C que chama essa função. Isso mostra domínio dos dois sentidos da integração. Em entrevista, esse projeto permite discutir `unsafe`, ABI, `repr(C)`, ponteiros, ownership, cross-compilation, CI e documentação de contratos. É muito mais forte do que apenas dizer que você "sabe Rust".

## Checklist antes de levar FFI para produção

Antes de chamar uma integração FFI de pronta, revise estes pontos:

- a superfície `unsafe` está isolada em poucos módulos;
- todo ponteiro nulo, tamanho inválido e código de erro é tratado;
- ownership de buffers, strings e handles está documentado;
- tipos que atravessam a ABI usam representação compatível, como `#[repr(C)]`;
- `panic` não atravessa a fronteira C;
- headers gerados ou consumidos são versionados junto do código;
- CI compila os exemplos do lado consumidor, não só o crate Rust;
- testes incluem acentos, entradas vazias, buffers grandes e falhas simuladas;
- release inclui bibliotecas nativas, headers e instruções de linking;
- a API pública Rust esconde detalhes brutos que não precisam vazar.

Rust FFI é uma das áreas onde a linguagem mostra seu valor com mais clareza: ela não promete apagar o legado por mágica, mas permite criar uma fronteira mais segura, testável e evolutiva ao redor dele. Em 2026, essa habilidade é especialmente útil para quem quer trabalhar com sistemas reais, onde código novo e código antigo precisam conviver por muitos anos.
