Skip to main content

traits | A palavra-chave impl | Rust

  • traits | A palavra-chave impl | Rust

Rust é uma linguagem de sistemas moderna, que evita herança tradicional e foca em composição de comportamentos por meio de traits. Esses comportamentos são associados a tipos através de blocos impl.


O que é trait?

Em Rust, uma trait define um conjunto de métodos que um tipo pode implementar. É semelhante ao conceito de interface em linguagens como C# ou Java.

Características:

  • Define o que um tipo pode fazer (contrato).
  • Pode conter implementações padrão (como interfaces com default em C# 8+).
  • Permite polimorfismo, via dispatch estático ou dinâmico.
  • Usada como restrição em generics (trait bounds).

📎 Exemplo:

trait Desenhavel {
fn desenhar(&self);
}

Com implementação:

struct Circulo;

impl Desenhavel for Circulo {
fn desenhar(&self) {
println!("Desenhando um círculo");
}
}

O que é impl?

A palavra-chave impl é usada para associar funcionalidades a tipos, sejam essas funcionalidades métodos nativos ou implementações de traits.


Tipos de impl em Rust

1. impl Tipo — Implementação intrínseca

Define métodos e funções associadas diretamente para um tipo, sem necessidade de trait.

struct Pessoa {
nome: String,
idade: u8,
}

impl Pessoa {
fn nova(nome: &str) -> Self {
Pessoa {
nome: nome.to_string(),
idade: 0,
}
}

fn fazer_aniversario(&mut self) {
self.idade += 1;
}
}

2. impl Trait for Tipo — Implementação de trait

Permite que um tipo "assine" o contrato de uma trait, fornecendo os métodos obrigatórios.

trait Saudavel {
fn calorias(&self) -> u32;
}

struct Fruta;

impl Saudavel for Fruta {
fn calorias(&self) -> u32 {
45
}
}

3. impl<T> — Implementações genéricas

Permite definir lógica reutilizável para múltiplos tipos:

struct Caixa<T> {
valor: T,
}

impl<T> Caixa<T> {
fn novo(valor: T) -> Self {
Caixa { valor }
}

fn pegar(&self) -> &T {
&self.valor
}
}

4. impl<T> where ... — Implementações condicionais

Aplica métodos apenas quando certas condições são satisfeitas.

use std::fmt::Display;

impl<T> Caixa<T>
where
T: Display,
{
fn imprimir(&self) {
println!("Valor: {}", self.valor);
}
}

5. default impl — Implementação padrão em traits

Traits podem oferecer comportamento padrão, que os tipos podem sobrescrever.

trait Saudavel {
fn calorias(&self) -> u32 {
100 // valor padrão
}
}

Comparação: Trait (Rust) vs Interface (C#)

CaracterísticaTrait (Rust)Interface (C#)
Contrato de métodos✔️✔️
Implementação padrão✔️ (desde Rust 1.0)✔️ (desde C# 8.0)
Múltiplas por tipo✔️✔️
Restrição em generics✔️ (T: Trait)✔️ (where T : IInterface)
Suporte a dispatch dinâmico✔️ (dyn Trait)✔️ (via virtual/interface)
Implementação externa✔️ (com limitações)
Composição (extends múltiplos)✔️ (via +)✔️ (múltiplas interfaces)

Uso de traits em generics

Com bound:

fn imprimir<T: std::fmt::Display>(valor: T) {
println!("{}", valor);
}

Com trait object (dispatch dinâmico):

fn imprimir(valor: &dyn std::fmt::Display) {
println!("{}", valor);
}

Composição de Traits

Você pode combinar múltiplos comportamentos:

fn processar<T: Desenhavel + Saudavel>(item: &T) {
item.desenhar();
println!("Calorias: {}", item.calorias());
}

Exemplo completo

trait Exibivel {
fn exibir(&self);
}

struct Produto {
nome: String,
preco: f64,
}

impl Exibivel for Produto {
fn exibir(&self) {
println!("{} - R$ {:.2}", self.nome, self.preco);
}
}

fn mostrar(item: &impl Exibivel) {
item.exibir();
}

Conclusão

  • Use trait para definir comportamentos genéricos.
  • Use impl para associar lógica a tipos.
  • Prefira generics com traits (impl Trait, T: Trait) para máxima performance.
  • Use dyn Trait apenas quando for necessária flexibilidade com custo de dispatch dinâmico.
  • Traits são mais poderosas que interfaces tradicionais, graças à composição, especialização, e implementação externa (limitada).