O que são Deref e DerefMut?
Os traits Deref e DerefMut controlam o comportamento do operador de dereferência (*). Eles são a base do sistema de smart pointers do Rust e habilitam o mecanismo de deref coercion — uma das conveniências mais poderosas da linguagem.
Deref: permite que*valorretorne uma referência ao tipo interno. Habilita coerção automática de&Tpara&U.DerefMut: extensão mutável deDeref. Permite que*valorretorne uma referência mutável.
Graças a Deref, você pode passar um &String onde um &str é esperado, ou um &Vec<T> onde um &[T] é esperado — tudo automaticamente.
Definição dos Traits
// std::ops::Deref
pub trait Deref {
type Target: ?Sized;
fn deref(&self) -> &Self::Target;
}
// std::ops::DerefMut
pub trait DerefMut: Deref {
fn deref_mut(&mut self) -> &mut Self::Target;
}
O tipo associado Target define para qual tipo a dereferência resolve. Por exemplo:
StringimplementaDeref<Target = str>Vec<T>implementaDeref<Target = [T]>Box<T>implementaDeref<Target = T>
Deref Coercion: A Mágica por trás dos Bastidores
Deref coercion é a conversão automática que o compilador aplica quando um tipo que implementa Deref é passado por referência. As regras são:
&T→&UquandoT: Deref<Target = U>&mut T→&mut UquandoT: DerefMut<Target = U>&mut T→&UquandoT: Deref<Target = U>(mutável para imutável)
A coerção nunca vai de &T para &mut U — isso violaria as regras de borrowing.
fn imprimir_tamanho(s: &str) {
println!("Tamanho: {}", s.len());
}
fn somar_slice(numeros: &[i32]) -> i32 {
numeros.iter().sum()
}
fn main() {
let texto = String::from("Rust Brasil");
let numeros = vec![1, 2, 3, 4, 5];
// Deref coercion: &String → &str
imprimir_tamanho(&texto);
// Deref coercion: &Vec<i32> → &[i32]
let soma = somar_slice(&numeros);
println!("Soma: {}", soma);
// Também funciona com Box
let boxed = Box::new(String::from("Boxed String"));
// Box<String> → String → str (cadeia de deref)
imprimir_tamanho(&boxed);
}
Como Implementar
Implementando Deref para um tipo wrapper
use std::ops::Deref;
struct MeuVec<T> {
interno: Vec<T>,
nome: String,
}
impl<T> MeuVec<T> {
fn novo(nome: impl Into<String>) -> Self {
MeuVec {
interno: Vec::new(),
nome: nome.into(),
}
}
fn push(&mut self, valor: T) {
self.interno.push(valor);
}
fn nome(&self) -> &str {
&self.nome
}
}
impl<T> Deref for MeuVec<T> {
type Target = Vec<T>;
fn deref(&self) -> &Vec<T> {
&self.interno
}
}
fn main() {
let mut v = MeuVec::novo("minha lista");
v.push(1);
v.push(2);
v.push(3);
// Métodos de Vec disponíveis via Deref
println!("Tamanho: {}", v.len()); // Vec::len()
println!("Primeiro: {:?}", v.first()); // slice::first()
println!("Nome: {}", v.nome()); // MeuVec::nome()
// Iteração funciona via Deref para &[T]
for item in v.iter() {
println!("{}", item);
}
}
Implementando Deref e DerefMut
use std::ops::{Deref, DerefMut};
use std::fmt;
#[derive(Debug)]
struct Validado<T: fmt::Debug> {
valor: T,
modificado: bool,
}
impl<T: fmt::Debug> Validado<T> {
fn novo(valor: T) -> Self {
Validado {
valor,
modificado: false,
}
}
fn foi_modificado(&self) -> bool {
self.modificado
}
}
impl<T: fmt::Debug> Deref for Validado<T> {
type Target = T;
fn deref(&self) -> &T {
&self.valor
}
}
impl<T: fmt::Debug> DerefMut for Validado<T> {
fn deref_mut(&mut self) -> &mut T {
self.modificado = true; // Rastreia modificações!
&mut self.valor
}
}
fn main() {
let mut nome = Validado::novo(String::from("Ana"));
// Deref: acessa métodos de String (imutável)
println!("Tamanho: {}", nome.len());
println!("Modificado? {}", nome.foi_modificado()); // false
// DerefMut: modifica o valor interno
nome.push_str(" Silva");
println!("Nome: {}", *nome);
println!("Modificado? {}", nome.foi_modificado()); // true
}
Exemplos Práticos
Exemplo 1: A cadeia de dereferência (Deref chaining)
O compilador aplica deref coercion repetidamente até encontrar o tipo esperado:
use std::rc::Rc;
fn contar_caracteres(s: &str) -> usize {
s.chars().count()
}
fn main() {
let texto = String::from("café");
// Cadeia de 1 nível: &String → &str
let n1 = contar_caracteres(&texto);
// Cadeia de 2 níveis: &Box<String> → &String → &str
let boxed = Box::new(texto.clone());
let n2 = contar_caracteres(&boxed);
// Cadeia de 3 níveis: &Rc<Box<String>> → &Box<String> → &String → &str
let rc = Rc::new(Box::new(texto));
let n3 = contar_caracteres(&rc);
println!("{} {} {}", n1, n2, n3); // 4 4 4
}
Exemplo 2: Smart pointer personalizado com contagem de acessos
use std::ops::Deref;
use std::cell::Cell;
struct Rastreado<T> {
valor: T,
acessos: Cell<u64>,
}
impl<T> Rastreado<T> {
fn novo(valor: T) -> Self {
Rastreado {
valor,
acessos: Cell::new(0),
}
}
fn total_acessos(&self) -> u64 {
self.acessos.get()
}
}
impl<T> Deref for Rastreado<T> {
type Target = T;
fn deref(&self) -> &T {
self.acessos.set(self.acessos.get() + 1);
&self.valor
}
}
fn main() {
let dados = Rastreado::novo(vec![10, 20, 30, 40, 50]);
// Cada acesso via Deref incrementa o contador
println!("Tamanho: {}", dados.len()); // acesso 1
println!("Primeiro: {:?}", dados.first()); // acesso 2
println!("Contém 30? {}", dados.contains(&30)); // acesso 3
println!("Total de acessos: {}", dados.total_acessos()); // 3
}
Exemplo 3: Deref com o newtype pattern
use std::ops::Deref;
use std::fmt;
struct Nome(String);
impl Deref for Nome {
type Target = str;
fn deref(&self) -> &str {
&self.0
}
}
impl fmt::Display for Nome {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// self automaticamente faz deref para &str
write!(f, "{}", &**self)
}
}
impl Nome {
fn novo(nome: impl Into<String>) -> Result<Self, String> {
let nome = nome.into();
if nome.trim().is_empty() {
Err("Nome não pode ser vazio".to_string())
} else {
Ok(Nome(nome))
}
}
}
fn cumprimentar(nome: &str) {
println!("Olá, {}!", nome);
}
fn main() {
let nome = Nome::novo("Maria").unwrap();
// Deref coercion: &Nome → &str
cumprimentar(&nome);
// Métodos de str disponíveis
println!("Maiúsculo: {}", nome.to_uppercase());
println!("Tamanho: {}", nome.len());
println!("Começa com 'M'? {}", nome.starts_with('M'));
}
Exemplo 4: String, Vec e Box — os Deref da stdlib
fn main() {
// String: Deref<Target = str>
let s = String::from("Rust é incrível");
let fatia: &str = &s; // Deref coercion
println!("{}", fatia);
// Vec<T>: Deref<Target = [T]>
let v = vec![1, 2, 3, 4, 5];
let slice: &[i32] = &v; // Deref coercion
println!("{:?}", slice);
// Métodos de slice disponíveis em Vec via Deref
println!("Janelas de 3:");
for janela in v.windows(3) {
println!(" {:?}", janela);
}
// Box<T>: Deref<Target = T>
let b = Box::new(42);
let valor: &i32 = &b; // Deref coercion
println!("Boxed: {}", valor);
// Box<dyn Trait> também funciona
let texto: Box<dyn std::fmt::Display> = Box::new("olá");
println!("{}", texto);
}
Exemplo 5: DerefMut na prática
use std::ops::{Deref, DerefMut};
struct LogVec<T> {
dados: Vec<T>,
}
impl<T: std::fmt::Debug> LogVec<T> {
fn novo() -> Self {
LogVec { dados: Vec::new() }
}
}
impl<T> Deref for LogVec<T> {
type Target = Vec<T>;
fn deref(&self) -> &Vec<T> {
&self.dados
}
}
impl<T: std::fmt::Debug> DerefMut for LogVec<T> {
fn deref_mut(&mut self) -> &mut Vec<T> {
&mut self.dados
}
}
fn main() {
let mut lista = LogVec::novo();
// DerefMut: métodos mutáveis de Vec
lista.push(1);
lista.push(2);
lista.push(3);
// Deref: métodos imutáveis
println!("Tamanho: {}", lista.len());
println!("Dados: {:?}", *lista);
// sort está disponível via DerefMut → &mut [T]
lista.sort();
lista.reverse();
println!("Invertido: {:?}", *lista); // [3, 2, 1]
}
Padrões e Boas Práticas
Implemente Deref apenas para smart pointers:
Derefé projetado para tipos que encapsulam outro tipo (comoBox,Rc,Arc). Não use para relações “tem um” arbitrárias.Deref não substitui herança: Embora deref coercion pareça herança, o propósito é diferente. Use traits para polimorfismo, não Deref.
Prefira
AsRefpara funções genéricas: Se você quer que uma função aceite tantoStringquanto&str, useAsRef<str>como bound em vez de depender de deref coercion. Veja AsRef e Borrow.DerefMut pode ter efeitos colaterais: Como no exemplo do tipo
Validado,deref_mutpode rastrear modificações. Use com cuidado.Cuidado com ambiguidade de métodos: Se seu tipo e o
Targettêm um método com o mesmo nome, o método do tipo tem prioridade. Use(*valor).metodo()para forçar o acesso via Deref.Deref é transitivo: O compilador aplica deref coercion em cadeia (
Rc<Box<String>>→Box<String>→String→str), mas isso pode dificultar a leitura do código.
Veja Também
- AsRef, AsMut e Borrow — alternativas a Deref para funções genéricas
- Operator Overloading: std::ops — Deref faz parte do módulo std::ops
- Box
— o smart pointer mais simples, implementa Deref - String — implementa Deref<Target = str>