0% acharam este documento útil (0 voto)
5 visualizações18 páginas

Python e Rust FFI

O documento aborda a integração entre Python e Rust através do Foreign Function Interface (FFI), explicando como Rust pode chamar e ser chamado por funções C. Ele destaca a diferença entre a interoperação dinâmica e permissiva do Python e a abordagem explícita e tipada do Rust, além de fornecer exemplos práticos de chamadas de funções e gerenciamento de memória. O texto também apresenta as melhores práticas para encapsular código inseguro em APIs seguras e discute o uso de PyO3 como uma forma idiomática de expor funções Rust como módulos Python.
Direitos autorais
© All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd
0% acharam este documento útil (0 voto)
5 visualizações18 páginas

Python e Rust FFI

O documento aborda a integração entre Python e Rust através do Foreign Function Interface (FFI), explicando como Rust pode chamar e ser chamado por funções C. Ele destaca a diferença entre a interoperação dinâmica e permissiva do Python e a abordagem explícita e tipada do Rust, além de fornecer exemplos práticos de chamadas de funções e gerenciamento de memória. O texto também apresenta as melhores práticas para encapsular código inseguro em APIs seguras e discute o uso de PyO3 como uma forma idiomática de expor funções Rust como módulos Python.
Direitos autorais
© All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd

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

Você também pode gostar