Deref e DerefMut Traits em Rust

Guia completo sobre Deref e DerefMut em Rust: smart pointers, deref coercion, String para &str, Vec para slice e exemplos.

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 *valor retorne uma referência ao tipo interno. Habilita coerção automática de &T para &U.
  • DerefMut: extensão mutável de Deref. Permite que *valor retorne 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:

  • String implementa Deref<Target = str>
  • Vec<T> implementa Deref<Target = [T]>
  • Box<T> implementa Deref<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:

  1. &T&U quando T: Deref<Target = U>
  2. &mut T&mut U quando T: DerefMut<Target = U>
  3. &mut T&U quando T: 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

  1. Implemente Deref apenas para smart pointers: Deref é projetado para tipos que encapsulam outro tipo (como Box, Rc, Arc). Não use para relações “tem um” arbitrárias.

  2. Deref não substitui herança: Embora deref coercion pareça herança, o propósito é diferente. Use traits para polimorfismo, não Deref.

  3. Prefira AsRef para funções genéricas: Se você quer que uma função aceite tanto String quanto &str, use AsRef<str> como bound em vez de depender de deref coercion. Veja AsRef e Borrow.

  4. DerefMut pode ter efeitos colaterais: Como no exemplo do tipo Validado, deref_mut pode rastrear modificações. Use com cuidado.

  5. Cuidado com ambiguidade de métodos: Se seu tipo e o Target tê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.

  6. Deref é transitivo: O compilador aplica deref coercion em cadeia (Rc<Box<String>>Box<String>Stringstr), mas isso pode dificultar a leitura do código.


Veja Também