Visao Geral do Modulo std::ptr
O modulo std::ptr fornece funcoes e tipos para trabalhar com ponteiros raw (*const T e *mut T) em Rust. Ponteiros raw sao o nivel mais baixo de acesso a memoria — eles nao carregam informacoes de lifetime, nao garantem validade e podem ser nulos.
Diferente de referencias (&T e &mut T), ponteiros raw:
- Podem ser nulos
- Podem apontar para memoria invalida
- Nao seguem regras de aliasing
- Podem ser criados em codigo safe, mas so podem ser desreferenciados em blocos
unsafe
O uso principal de std::ptr e em:
- FFI (Foreign Function Interface) — interoperabilidade com C
- Implementacao de estruturas de dados — listas ligadas, alocadores customizados
- Otimizacoes de baixo nivel — quando as abstracoes safe tem custo inaceitavel
Aviso: Todo desreferenciamento de ponteiro raw requer unsafe. Use ponteiros raw apenas quando nao houver alternativa safe. Quando usar, documente as invariantes de seguranca com comentarios // SAFETY:.
Tipos e Funcoes Principais
Tipos de Ponteiro
| Tipo | Descricao |
|---|---|
*const T | Ponteiro raw imutavel |
*mut T | Ponteiro raw mutavel |
NonNull<T> | Ponteiro nao-nulo (otimizado para Option) |
Funcoes do Modulo
| Funcao | Descricao |
|---|---|
null::<T>() | Cria um *const T nulo |
null_mut::<T>() | Cria um *mut T nulo |
read(src) | Le o valor apontado (unsafe) |
write(dst, val) | Escreve um valor no destino (unsafe) |
read_unaligned(src) | Le de endereco nao-alinhado (unsafe) |
write_unaligned(dst, val) | Escreve em endereco nao-alinhado (unsafe) |
copy(src, dst, count) | Copia count elementos (regioes podem se sobrepor, unsafe) |
copy_nonoverlapping(src, dst, count) | Copia sem sobreposicao (unsafe) |
swap(a, b) | Troca valores via ponteiros (unsafe) |
drop_in_place(ptr) | Chama o destrutor do valor apontado (unsafe) |
addr_of!(expr) | Cria *const T sem criar referencia |
addr_of_mut!(expr) | Cria *mut T sem criar referencia |
Metodos de Ponteiro
| Metodo | Descricao |
|---|---|
.is_null() | Verifica se e nulo |
.offset(count) | Desloca por count elementos (unsafe) |
.add(count) | Avanca count elementos (unsafe) |
.sub(count) | Retrocede count elementos (unsafe) |
.as_ref() | Converte para Option<&T> (unsafe) |
.as_mut() | Converte para Option<&mut T> (unsafe) |
.cast::<U>() | Converte para *const U ou *mut U |
Exemplos Praticos
1. Criando e Usando Ponteiros Raw
fn main() {
let valor = 42i32;
let referencia = &valor;
// Criando ponteiros raw a partir de referencias (safe)
let ptr_const: *const i32 = &valor;
let ptr_const2: *const i32 = referencia as *const i32;
// Verificando nulidade (safe)
println!("E nulo? {}", ptr_const.is_null()); // false
// Desreferenciar requer unsafe
// SAFETY: ptr_const foi criado a partir de uma referencia valida
// e o valor original ainda esta vivo
unsafe {
println!("Valor: {}", *ptr_const); // 42
}
// Ponteiro mutavel
let mut numero = 10;
let ptr_mut: *mut i32 = &mut numero;
// SAFETY: ptr_mut aponta para 'numero' que esta vivo e acessivel
unsafe {
*ptr_mut = 20;
}
println!("Modificado: {numero}"); // 20
// Ponteiro nulo
let nulo: *const i32 = std::ptr::null();
println!("E nulo? {}", nulo.is_null()); // true
}
2. NonNull — Ponteiro Garantidamente Nao-Nulo
use std::ptr::NonNull;
struct Node<T> {
valor: T,
proximo: Option<NonNull<Node<T>>>,
}
impl<T: std::fmt::Display> Node<T> {
fn new(valor: T) -> Self {
Node { valor, proximo: None }
}
fn imprimir_lista(&self) {
print!("{}", self.valor);
let mut atual = self.proximo;
while let Some(ptr) = atual {
// SAFETY: NonNull garante que o ponteiro nao e nulo,
// e mantemos a invariante de que todos os nos sao validos
unsafe {
let node = ptr.as_ref();
print!(" -> {}", node.valor);
atual = node.proximo;
}
}
println!();
}
}
fn main() {
let mut n1 = Node::new(1);
let mut n2 = Node::new(2);
let n3 = Node::new(3);
// Conectando os nos
n2.proximo = NonNull::new(&n3 as *const Node<i32> as *mut Node<i32>);
n1.proximo = NonNull::new(&n2 as *const Node<i32> as *mut Node<i32>);
n1.imprimir_lista(); // 1 -> 2 -> 3
// NonNull tem o mesmo tamanho que *mut T
println!(
"Tamanho de Option<NonNull<i32>>: {} bytes",
std::mem::size_of::<Option<NonNull<i32>>>()
); // 8 (mesma otimizacao que Option<&T>)
}
3. Aritmetica de Ponteiros e Acesso a Arrays
fn main() {
let dados = [10, 20, 30, 40, 50];
let ptr = dados.as_ptr();
// Acessando elementos via aritmetica de ponteiros
for i in 0..dados.len() {
// SAFETY: i esta dentro dos limites do array,
// entao ptr.add(i) aponta para um elemento valido
unsafe {
let valor = *ptr.add(i);
println!("dados[{i}] = {valor}");
}
}
// Copiando dados entre arrays
let origem = [1u32, 2, 3, 4];
let mut destino = [0u32; 4];
// SAFETY: origem e destino nao se sobrepoe em memoria,
// ambos tem pelo menos 4 elementos de u32
unsafe {
std::ptr::copy_nonoverlapping(
origem.as_ptr(),
destino.as_mut_ptr(),
4,
);
}
println!("Destino: {destino:?}"); // [1, 2, 3, 4]
}
4. Padrao FFI — Interoperabilidade com C
use std::ffi::CStr;
use std::os::raw::c_char;
// Simulando uma funcao C que retorna uma string
fn funcao_c_simulada() -> *const c_char {
b"Resultado da funcao C\0".as_ptr() as *const c_char
}
// Simulando uma funcao C que recebe um ponteiro para preencher
fn c_preencher_buffer(buf: *mut i32, tamanho: usize) {
for i in 0..tamanho {
// SAFETY: assumimos que buf aponta para memoria valida
// com pelo menos 'tamanho' elementos
unsafe {
buf.add(i).write(i as i32 * 100);
}
}
}
fn main() {
// Recebendo string de C
let ptr_c = funcao_c_simulada();
if !ptr_c.is_null() {
// SAFETY: verificamos que nao e nulo e a string
// termina com null byte
let texto = unsafe { CStr::from_ptr(ptr_c) };
println!("{}", texto.to_string_lossy());
}
// Passando buffer para funcao C preencher
let mut buffer = vec![0i32; 5];
// SAFETY: buffer tem exatamente 5 elementos alocados
c_preencher_buffer(buffer.as_mut_ptr(), buffer.len());
println!("Buffer: {buffer:?}"); // [0, 100, 200, 300, 400]
}
5. addr_of! e addr_of_mut! para Campos Nao-Alinhados
use std::ptr;
#[repr(packed)]
struct Pacote {
flag: u8,
valor: u32, // Nao alinhado em 4 bytes por causa de #[repr(packed)]
dados: u64,
}
fn main() {
let pacote = Pacote {
flag: 1,
valor: 42,
dados: 0xDEADBEEF,
};
// ERRADO: &pacote.valor criaria uma referencia nao-alinhada (UB!)
// let ref_valor = &pacote.valor; // Isso e UB em repr(packed)!
// CORRETO: addr_of! cria um ponteiro raw sem criar referencia
let ptr_valor = ptr::addr_of!(pacote.valor);
// SAFETY: usamos read_unaligned porque o campo pode nao estar alinhado
let valor = unsafe { ptr::read_unaligned(ptr_valor) };
println!("Valor: {valor}"); // 42
let ptr_dados = ptr::addr_of!(pacote.dados);
let dados = unsafe { ptr::read_unaligned(ptr_dados) };
println!("Dados: {dados:#X}"); // 0xDEADBEEF
}
Padroes Comuns
Verificacao de Nulidade Antes de Usar
Sempre verifique ponteiros recebidos de FFI:
fn processar_ponteiro(ptr: *const i32) -> Option<i32> {
if ptr.is_null() {
return None;
}
// SAFETY: acabamos de verificar que nao e nulo.
// Assumimos que o chamador garante que o ponteiro
// aponta para memoria valida.
Some(unsafe { *ptr })
}
Convertendo Entre Ponteiros e Referencias
fn ponteiro_para_referencia<'a>(ptr: *const i32) -> Option<&'a i32> {
// SAFETY: as_ref() retorna None se o ponteiro for nulo.
// O chamador deve garantir que o ponteiro aponta para
// memoria valida pelo lifetime 'a.
unsafe { ptr.as_ref() }
}
Usando NonNull como Campo de Struct
use std::ptr::NonNull;
struct Buffer {
ptr: NonNull<u8>,
len: usize,
}
// SAFETY: NonNull nao implementa Send/Sync automaticamente
// Implementamos apenas se tivermos ownership exclusivo
unsafe impl Send for Buffer {}
unsafe impl Sync for Buffer {}
Quando Usar (e Quando Nao Usar)
Use std::ptr quando:
- Estiver fazendo FFI com C/C++
- Implementando estruturas de dados com ponteiros (listas, arvores)
- Precisar de
addr_of!para structs#[repr(packed)] - Implementando alocadores de memoria customizados
- Otimizacoes de performance comprovadamente necessarias
Nao use std::ptr quando:
- Referencias (
&T,&mut T) resolverem o problema Box,Rc,Arcatenderem a necessidade de ownership- Estiver tentando contornar o borrow checker (repense o design!)
Vec,HashMapou outra colecao da stdlib for suficiente
Regra de ouro: Se voce precisa de ponteiros raw e nao esta fazendo FFI ou implementando uma estrutura de dados fundamental, provavelmente existe uma alternativa safe melhor.
Veja Tambem
- Unsafe Rust — guia completo sobre blocos unsafe
- Modulo std::mem — funcoes de manipulacao de memoria
- Modulo std::marker — traits como Send e Sync para ponteiros
- Modulo std::any — alternativa safe para type erasure
- Modulo std::convert — conversoes seguras entre tipos
- Documentacao oficial — std::ptr