Capítulo 19: Unsafe Rust, Ponteiros Brutos e FFI (C Interop)
Capítulo 19: Unsafe Rust, Ponteiros Brutos e FFI (C Interop)
Especialização em Sistemas com Rust • Rust 2021 Edition • Ownership, Concorrência Segura e Sistemas de Alta Performance
🗺️ Mapa Conceitual do Encapsulamento Seguro em Unsafe Rust e FFI
flowchart TD
CodigoCliente["Código Seguro do Usuário (Safe Rust)"] --> ApiSegura["API Pública Segura (Contratos Fortes e Invariantes)"]
subgraph ISOLAMENTO_UNSAFE["Fronteira Estrita de Isolamento (Unsafe Boundary)"]
BlocoUnsafe["Bloco unsafe { ... }"]
RawPtrs["Ponteiros Brutos: *const T e *mut T"]
FFI_C["Chamadas Externas: extern 'C' (libc, Windows API, Drivers)"]
end
ApiSegura --> ISOLAMENTO_UNSAFE
ISOLAMENTO_UNSAFE --> HardwareNativo["Acesso Direto à Memória / Registradores / SO"]
ApiSegura -.->|"Blindagem Total: Erros Não Vazam para o Usuário"| CodigoCliente
style CodigoCliente fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
style ApiSegura fill:#e8f5e9,stroke:#4caf50,stroke-width:2px
style ISOLAMENTO_UNSAFE fill:#fff3e0,stroke:#ff9800,stroke-width:2px
style BlocoUnsafe fill:#ffebee,stroke:#f44336,stroke-width:2px
style HardwareNativo fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px
🏛️ 1. O que é o Unsafe Rust e Por que Ele Existe?
O compilador do Rust é deliberadamente conservador: se ele não conseguir provar matematicamente que um trecho de código é 100% seguro contra violações de memória, ele rejeita a compilação, mesmo que o programador saiba que a operação é válida.
No entanto, sistemas operacionais, drivers de vídeo, motores de bancos de dados e rotinas de comunicação direta com registradores de hardware precisam realizar operações de baixo nível que o compilador não consegue auditar sozinho. Para esses cenários, o Rust fornece uma válvula de escape controlada: o Unsafe Rust.
O que o unsafe NÃO Faz:
- O
unsafenão desativa o Borrow Checker. - O
unsafenão desativa a checagem de tipos. - O
unsafeapenas concede acesso a 5 superpoderes restritos.
⚡ 2. Os 5 Superpoderes do unsafe
Dentro de um bloco unsafe { ... }, você tem permissão exclusiva para:
- Desreferenciar Ponteiros Brutos (Raw Pointers): Ler ou escrever em ponteiros
*const Te*mut T. - Invocar Funções ou Métodos
unsafe: Incluindo chamadas de bibliotecas escritas em C/C++ (FFI). - Implementar Traits
unsafe: Como garantir manualmente que um tipo customizado éSendouSync. - Mutar Variáveis Estáticas Mutáveis: Manipular
static mut. - Acessar Campos de uma
union: Estruturas de estilo C com sobreposição de memória.
📍 3. Ponteiros Brutos: *const T e *mut T
Diferente das referências normais (&T e &mut T), os ponteiros brutos:
- Podem ser nulos (null pointers).
- Podem ter alinhamento arbitrário de memória.
- Não possuem lifetimes e não são rastreados pelo Borrow Checker.
- Não possuem garantia de limpeza automática (não executam
Drop).
let mut numero: i32 = 42;
// Criar ponteiros brutos é 100% seguro e NÃO exige 'unsafe':
let ptr_imutavel: *const i32 = &numero as *const i32;
let ptr_mutavel: *mut i32 = &mut numero as *mut i32;
// APENAS desreferenciar (ler ou escrever no endereço) exige o bloco 'unsafe':
unsafe {
println!("Lendo valor via ponteiro bruto: {}", *ptr_imutavel);
*ptr_mutavel += 58;
println!("Novo valor após escrita direta: {}", *ptr_mutavel);
}
🌉 4. FFI: Interoperabilidade Direta com a Linguagem C
Para se comunicar com bibliotecas legadas do sistema operacional (Windows API, POSIX, OpenSSL ou libc), o Rust utiliza o bloco extern "C":
use std::ffi::{CStr, CString};
// Declaração de assinaturas de funções C externas
extern "C" {
fn puts(s: *const i8) -> i32;
fn abs(x: i32) -> i32;
}
fn main() {
// 1. Invocando função simples de C
let valor_abs = unsafe { abs(-99) };
println!("Resultado de abs(-99) em C: {}", valor_abs);
// 2. Passando strings compatíveis com C (terminadas em null byte '\0')
let mensagem = CString::new("Rust comunicando diretamente com o kernel C!").unwrap();
unsafe {
puts(mensagem.as_ptr());
}
}
4.1 O Atributo #[repr(C)]
Ao compartilhar structs com código em C, é mandatório anotar a struct com #[repr(C)] para instruir o compilador a organizar os campos com o mesmo alinhamento de memória e padding adotados pelos compiladores C (GCC/Clang/MSVC):
#[repr(C)]
pub struct CabecalhoPacoteC {
pub versao: u8,
pub flags: u8,
pub checksum: u16,
pub tamanho_payload: u32,
}
🛡️ 5. O Princípio da Abstração Segura (Safe Abstraction)
Na engenharia de software profissional em Rust, a regra suprema é:
O código
unsafedeve ser restrito ao menor escopo possível e encapsulado dentro de uma API pública 100% segura.
O usuário que consome sua biblioteca jamais deve ser obrigado a escrever unsafe. A sua struct pública deve garantir todas as invariantes de tamanho, limites de array e validade de ponteiros internamente.
⚖️ 6. Matriz Comparativa: Referências vs Raw Pointers
| Característica | Referências Seguras (&T, &mut T) |
Ponteiros Brutos (*const T, *mut T) |
|---|---|---|
| Pode Ser Nulo? | NUNCA (Garantia matemática) | SIM (std::ptr::null()) |
| Rastreado pelo Borrow Checker? | SIM (Aliasing XOR Mutability) | NÃO (Você assume a responsabilidade) |
| Validação de Lifetimes? | SIM | NÃO |
Exige Bloco unsafe para Criar? |
Não | Não |
Exige Bloco unsafe para Desreferenciar? |
Não | SIM |
⚠️ 7. Armadilhas Críticas e o Perigo do Undefined Behavior (UB)
Escrever código unsafe incorreto pode resultar em Undefined Behavior (Comportamento Indefinido - UB), onde o compilador LLVM assume otimizações falsas que corrompem a execução.
- Exemplos de UB: Criar referências sobrepostas não-alinhadas, violar a regra de unicidade de
&mutmesmo dentro de um bloco unsafe, ou ler memória não inicializada. - Ferramenta de Auditoria: Utilize o Miri (
cargo miri test), o interpretador oficial do Rust que executa sua suíte de testes detectando qualquer violação de regras de ponteiros e UB em tempo de execução!
📋 8. Cheat-Sheet: Resumo Executivo
// Ponteiro bruto
let mut x = 10;
let p: *mut i32 = &mut x;
unsafe { *p = 20; }
// FFI com C
extern "C" { fn getpid() -> i32; }
let pid = unsafe { getpid() };
// Struct compatível com C
#[repr(C)]
struct PontoC { x: f32, y: f32 }