11/03/2026, 12:49 Python e Rust FFI
ChatGPT 5.4 Thinking Memória desativada
Sou programador Python aprendendo Rust, tenha ótima
didática e faça correlação entre as linguagens, mostre
como o rust trabalha comSou programador Python
aprendendo Rust, tenha ótima didática e faça correlação
entre as linguagens, mostre como o rust trabalha com FFI
(Foreign Function Interface)
Vou te explicar do jeito mais prático possível: primeiro a ideia de FFI, depois o paralelo
mental com Python, e por fim exemplos mínimos em Rust chamando C e sendo chamado
por C.
Pensou por 7s
Perfeito. Vamos construir isso com a cabeça de quem já pensa em Python, mas quer
entender como Rust conversa com código externo de forma segura e previsível.
Visão geral: o que é FFI
FFI (Foreign Function Interface) é a ponte que permite uma linguagem chamar código
escrito em outra linguagem.
Na prática, com Rust, isso normalmente significa:
Rust chamando funções C
C chamando funções Rust
e, indiretamente, Python conversando com Rust via bibliotecas nativas
Mapa mental com Python
Em Python, você já viu algo parecido com:
ctypes
cffi
extensões em C
PyO3 / maturin para criar módulos Python em Rust
A diferença é que:
em Python, a interop costuma ser mais dinâmica e permissiva
em Rust, a interop é explícita, tipada e geralmente passa por blocos unsafe
Ou seja:
[Link] 1/18
11/03/2026, 12:49 Python e Rust FFI
Python confia mais em tempo de execução.
Rust quer deixar bem claro onde existe risco.
A ideia central do FFI em Rust
Quando Rust fala com outra linguagem, ele precisa combinar 3 coisas:
1. ABI compatível
ABI é o “jeito binário” como função recebe argumentos, devolve valores, organiza pilha,
etc.
Em Rust, você normalmente declara:
Rust
extern "C"
Isso diz:
“essa função usa a convenção de chamada da linguagem C”
É muito importante porque o C virou o “idioma universal” da interoperabilidade entre
linguagens.
2. Layout de memória compatível
Se você trocar structs entre Rust e C, o layout da struct precisa bater.
Para isso, usa-se:
Rust
#[repr(C)]
Isso força o Rust a organizar os campos como C espera.
3. Bloco unsafe
Chamadas FFI quase sempre envolvem unsafe , porque o compilador Rust não consegue
garantir sozinho que:
[Link] 2/18
11/03/2026, 12:49 Python e Rust FFI
ponteiros são válidos
tamanhos estão corretos
a outra linguagem respeita contratos
não houve uso após liberação de memória
Então o Rust exige que você assuma responsabilidade explicitamente.
Comparação direta com Python
Python
Exemplo mental com ctypes :
Python Executar
from ctypes import CDLL
lib = CDLL("./minha_biblioteca.so")
resultado = [Link](2, 3)
Você chama e pronto. Se passar tipo errado, pode falhar em runtime.
Rust
Em Rust, você faria algo como:
Rust
unsafe {
let r = soma(2, 3);
}
Mas antes precisa declarar a assinatura corretamente.
Caso 1: Rust chamando uma função C
Esse é o primeiro cenário clássico.
Código C
Arquivo math.c :
[Link] 3/18
11/03/2026, 12:49 Python e Rust FFI
int soma(int a, int b) {
return a + b;
}
Compile para objeto ou biblioteca.
Declaração em Rust
Arquivo [Link] :
Rust
unsafe extern "C" {
fn soma(a: i32, b: i32) -> i32;
}
fn main() {
let resultado = unsafe { soma(10, 20) };
println!("Resultado: {}", resultado);
}
O que está acontecendo
Em Python, seu raciocínio seria:
“Estou importando uma função externa e chamando.”
Em Rust, o raciocínio correto é:
“Estou dizendo ao compilador que existe uma função externa com essa assinatura e ABI C.
Agora eu assumo o risco da chamada.”
Por que unsafe extern "C" ?
Porque Rust não pode provar que a função externa:
realmente existe
usa essa assinatura
respeita esse contrato
Então a chamada exige unsafe .
Caso 2: Rust expondo função para C
[Link] 4/18
11/03/2026, 12:49 Python e Rust FFI
Agora o caminho inverso: C ou outra linguagem chama Rust.
Exemplo Rust
Rust
#[unsafe(no_mangle)]
pub extern "C" fn soma(a: i32, b: i32) -> i32 {
a + b
}
Entendendo cada parte
pub
Deixa a função visível externamente.
extern "C"
Usa ABI C.
no_mangle
Rust normalmente altera o nome interno da função durante compilação.
no_mangle impede isso, preservando o nome soma .
Sem isso, o linker externo talvez não encontre a função.
Comparação com Python
Em Python, você raramente pensa em:
nome exportado no binário
ABI
mangling
linker
Em Rust, isso faz parte do jogo porque você está mais perto do sistema.
Strings em FFI: onde começam os problemas reais
Aqui está um dos pontos mais importantes.
Em Python
[Link] 5/18
11/03/2026, 12:49 Python e Rust FFI
String é um objeto rico, gerenciado pela VM.
Em Rust
String não é compatível diretamente com C.
C espera normalmente:
char*
ou seja, ponteiro para bytes terminados com \0 .
Em Rust, para interoperar com C, você usa:
CString para enviar string ao C
CStr para receber string do C
Enviando string Rust para C
Rust
use std::ffi::CString;
use std::os::raw::c_char;
unsafe extern "C" {
fn imprimir(msg: *const c_char);
}
fn main() {
let s = CString::new("Olá do Rust").unwrap();
unsafe {
imprimir(s.as_ptr());
}
}
Explicação didática
No Python, seria como:
converter para um buffer compatível com C antes de chamar uma função nativa.
No Rust:
CString garante que a string:
[Link] 6/18
11/03/2026, 12:49 Python e Rust FFI
não contém \0 interno inválido
termina com \0
pode virar *const c_char
Recebendo string C no Rust
Rust
use std::ffi::CStr;
use std::os::raw::c_char;
unsafe fn ler_string(ptr: *const c_char) {
let c_str = unsafe { CStr::from_ptr(ptr) };
let texto = c_str.to_str().unwrap();
println!("Recebido: {}", texto);
}
Atenção aqui
CStr::from_ptr(ptr) é perigoso se:
ptr for nulo
não apontar para memória válida
não houver terminador \0
Por isso é unsafe .
Structs em FFI
Em Python
Você até pode representar structs via [Link] , mas geralmente pensa menos
nisso.
Em Rust
Você precisa garantir layout binário compatível.
Exemplo
[Link] 7/18
11/03/2026, 12:49 Python e Rust FFI
typedef struct {
int x;
int y;
} Ponto;
Rust
Rust
#[repr(C)]
pub struct Ponto {
pub x: i32,
pub y: i32,
}
Sem #[repr(C)] , Rust pode reorganizar detalhes internos de forma não compatível com
C.
Ponteiros: comparação mental com Python
Em Python puro, você quase não lida com ponteiro cru.
Em Rust FFI, você verá muito:
*const T
*mut T
Significado
*const T → ponteiro somente leitura
*mut T → ponteiro mutável
Esses são raw pointers, diferentes de referências Rust:
&T
&mut T
Diferença importante
Referências Rust têm garantias de segurança.
Raw pointers não.
Por isso, dereferenciar raw pointer exige unsafe .
[Link] 8/18
11/03/2026, 12:49 Python e Rust FFI
Exemplo: função Rust recebendo ponteiro
Rust
#[unsafe(no_mangle)]
pub extern "C" fn dobrar(valor: *mut i32) {
unsafe {
if !valor.is_null() {
*valor *= 2;
}
}
}
O que isso faz
Recebe um ponteiro para i32 e altera o valor na memória.
Mentalmente comparando com Python
Seria parecido com passar um endereço para uma função C usando [Link] .
Mas Rust é muito mais explícito sobre o risco.
Gerenciamento de memória: o ponto mais crítico
Esse é o lugar onde muita gente erra em FFI.
A regra de ouro é:
Quem aloca deve, idealmente, desalocar.
Exemplo do problema
Se Rust cria memória e C tenta liberar com free() , pode dar desastre.
Se C cria memória e Rust tenta liberar como se fosse Rust-owned, também pode dar
desastre.
Porque cada lado pode usar alocadores diferentes.
Estratégia segura
Quando precisar transferir ownership entre linguagens, crie funções explícitas:
uma para criar
[Link] 9/18
11/03/2026, 12:49 Python e Rust FFI
outra para destruir
Exemplo
Rust
use std::ffi::CString;
use std::os::raw::c_char;
#[unsafe(no_mangle)]
pub extern "C" fn saudacao() -> *mut c_char {
let s = CString::new("Olá vindo do Rust").unwrap();
s.into_raw()
}
#[unsafe(no_mangle)]
pub extern "C" fn liberar_string(ptr: *mut c_char) {
if ptr.is_null() {
return;
}
unsafe {
let _ = CString::from_raw(ptr);
}
}
Explicação
into_raw()
Entrega o ponteiro ao mundo externo e impede que Rust libere automaticamente.
from_raw()
Reconstrói o CString para que Rust libere corretamente ao sair do escopo.
Correlação forte com Python
Quando você usa Python com extensão nativa, isso também existe, só que muitas vezes
escondido.
Exemplo mental:
Python chama código C/Rust
esse código devolve ponteiro ou objeto
alguém precisa saber quem é o dono da memória
Em Rust isso fica mais explícito e, honestamente, isso é uma vantagem.
[Link] 10/18
11/03/2026, 12:49 Python e Rust FFI
unsafe não significa “errado”
Isso é importante.
Muita gente vindo de Python olha unsafe e pensa: “isso é proibido?”
Não.
Significa:
“o compilador não consegue verificar esta parte; agora a responsabilidade é sua.”
Você pode escrever FFI excelente e robusto em Rust, desde que mantenha a parte
insegura:
pequena
isolada
bem documentada
envolvida por APIs seguras
Boa prática: encapsular FFI inseguro em API segura
Ruim
Espalhar unsafe pelo projeto todo.
Bom
Criar uma camada pequena de interop e expor funções seguras para o resto do código.
Exemplo
Rust
use std::ffi::CString;
use std::os::raw::c_char;
unsafe extern "C" {
fn imprimir(msg: *const c_char);
}
pub fn imprimir_seguro(texto: &str) {
let c_string = CString::new(texto).expect("string contém byte nulo");
unsafe {
imprimir(c_string.as_ptr());
[Link] 11/18
11/03/2026, 12:49 Python e Rust FFI
}
}
Agora o resto do programa usa só:
Rust
fn main() {
imprimir_seguro("Olá");
}
Tipos C mais comuns em Rust
Rust costuma usar os tipos do módulo std::os::raw ou equivalentes de core::ffi .
Exemplos:
Rust
use std::os::raw::{c_char, c_int, c_void};
Paralelo
c_int ≈ int em C
c_char ≈ char
c_void ≈ void
Isso evita assumir tamanhos errados.
FFI com Python: onde Rust entra de verdade
Como você é programador Python, essa é a parte que mais vale ouro.
Há duas formas principais de integrar Python e Rust:
1. Python chama uma biblioteca Rust via C ABI
Você gera uma .dll , .so ou .dylib com funções extern "C" e usa:
ctypes
cffi
Vantagem
[Link] 12/18
11/03/2026, 12:49 Python e Rust FFI
Simples conceitualmente, universal.
Desvantagem
Você trabalha com ponteiros, buffers, strings C, etc.
2. Usar PyO3
PyO3 é a forma mais idiomática hoje para expor funções Rust como módulo Python.
Você escreve algo como:
Rust
use pyo3::prelude::*;
#[pyfunction]
fn soma(a: i32, b: i32) -> i32 {
a + b
}
#[pymodule]
fn meu_modulo(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(soma, m)?)?;
Ok(())
}
E no Python:
Python Executar
import meu_modulo
print(meu_modulo.soma(2, 3))
Correlação importante
Via C ABI
Você trabalha no nível mais baixo, parecido com ctypes .
Via PyO3
Você trabalha num nível mais alto, parecido com criar extensão Python de forma moderna.
Quando usar cada abordagem
[Link] 13/18
11/03/2026, 12:49 Python e Rust FFI
Use C ABI/FFI direto quando:
precisa compatibilidade com várias linguagens
quer uma biblioteca nativa genérica
vai integrar com sistemas legados em C/C++
quer máxima neutralidade de linguagem
Use PyO3 quando:
o alvo principal é Python
você quer ergonomia
quer menos sofrimento com strings e objetos Python
deseja publicar extensão Python mais facilmente
Exemplo mental completo: Python vs Rust em FFI
Em Python com ctypes
Você pensa:
1. carregar biblioteca
2. descrever assinatura
3. chamar função
4. torcer para tipos e memória estarem corretos
Em Rust
Você pensa:
1. definir ABI
2. declarar tipos compatíveis
3. garantir layout e ownership
4. isolar unsafe
5. expor interface segura
Rust te obriga a pensar mais cedo sobre coisas que em Python às vezes só quebram
depois.
Cuidados fundamentais em FFI com Rust
1. Nunca passe String ou &str diretamente para C
[Link] 14/18
11/03/2026, 12:49 Python e Rust FFI
Use CString / CStr .
2. Nunca passe structs sem #[repr(C)]
Ou o layout pode ficar incompatível.
3. Nunca esqueça quem é dono da memória
Ownership é tudo.
4. Minimize unsafe
Deixe concentrado em poucas funções.
5. Valide ponteiros nulos
Sempre que receber ponteiro externo.
6. Cuidado com panics atravessando FFI
Em geral, você não deve deixar panic atravessar fronteira FFI.
Uma prática comum é capturar erros internamente e retornar códigos de erro.
Exemplo mais realista: API estilo C feita em Rust
Rust
use std::ffi::{CStr, CString};
use std::os::raw::{c_char, c_int};
#[unsafe(no_mangle)]
pub extern "C" fn contar_bytes(s: *const c_char) -> c_int {
if s.is_null() {
return -1;
}
let c_str = unsafe { CStr::from_ptr(s) };
match c_str.to_str() {
Ok(texto) => [Link]() as c_int,
Err(_) => -2,
}
}
#[unsafe(no_mangle)]
pub extern "C" fn mensagem() -> *mut c_char {
CString::new("Olá do Rust").unwrap().into_raw()
}
[Link] 15/18
11/03/2026, 12:49 Python e Rust FFI
#[unsafe(no_mangle)]
pub extern "C" fn liberar_mensagem(ptr: *mut c_char) {
if ptr.is_null() {
return;
}
unsafe {
let _ = CString::from_raw(ptr);
}
}
O que esse exemplo ensina
entrada de string C
validação de ponteiro nulo
conversão segura para UTF-8
retorno de string alocada por Rust
função dedicada para liberar memória
Isso é muito representativo de FFI real.
Como pensar em FFI vindo de Python
Quero te passar um modelo mental muito útil:
Python te acostuma a pensar em:
objetos
referências abstratas
GC / refcount
runtime resolvendo muita coisa
Rust te força a pensar em:
layout de memória
ABI
ownership
validade de ponteiros
fronteira entre código seguro e inseguro
Essa mudança é exatamente o que faz Rust ser tão bom para sistemas, extensões nativas e
alto desempenho.
[Link] 16/18
11/03/2026, 12:49 Python e Rust FFI
Resumo direto
FFI em Rust gira em torno de:
extern "C" → ABI compatível com C
#[repr(C)] → layout de memória compatível
#[unsafe(no_mangle)] → exportar nome previsível
unsafe → assumir responsabilidade pela fronteira externa
CString / CStr → strings compatíveis com C
cuidado absoluto com ownership e liberação de memória
Regra prática para nunca se perder
Quando estiver fazendo FFI, pergunte sempre:
1. Qual é a ABI?
2. Os tipos binários batem?
3. Quem é dono da memória?
4. Quem libera a memória?
5. Este ponteiro é válido?
6. Posso encapsular isso numa API segura?
Se você responder essas 6 perguntas, já evita a maioria dos erros graves.
Melhor correlação Python → Rust para memorizar
Python
“Eu chamo biblioteca nativa.”
Rust
“Eu defino um contrato binário explícito entre dois mundos.”
Essa é a diferença central.
Próximo passo recomendado
A sequência ideal de estudo é:
1. Rust chamando função C simples
[Link] 17/18
11/03/2026, 12:49 Python e Rust FFI
2. Rust recebendo string C
3. Rust exportando função para C
4. ownership de memória entre linguagens
5. PyO3 para integrar com Python
Posso montar para você o próximo passo de forma prática com um projeto completo em
Windows, mostrando:
[Link]
código Rust
compilação para DLL
chamada a partir de Python com ctypes
e depois a versão equivalente com PyO3.
[Link] 18/18