---
title: "Operator Overloading em Rust: std::ops"
url: "https://rustlang.com.br/stdlib/ops-traits/"
markdown_url: "https://rustlang.com.br/stdlib/ops-traits.MD"
description: "Guia completo sobre sobrecarga de operadores em Rust: traits Add, Sub, Mul, Div, Neg, Index, IndexMut e o newtype pattern."
date: "2026-02-23"
author: "Equipe Rust Brasil"
---

# Operator Overloading em Rust: std::ops

Guia completo sobre sobrecarga de operadores em Rust: traits Add, Sub, Mul, Div, Neg, Index, IndexMut e o newtype pattern.


## O que são os Traits de Operadores?

Em Rust, operadores como `+`, `-`, `*`, `/`, `[]` e `-` (negação) não são mágicos — eles são chamadas a métodos definidos por traits no módulo `std::ops`. Quando você escreve `a + b`, o compilador traduz isso para `a.add(b)`.

Isso significa que você pode definir o comportamento de operadores para seus próprios tipos implementando os traits correspondentes. Essa técnica é chamada de **operator overloading** (sobrecarga de operadores).

Os principais traits de `std::ops`:

| Operador | Trait | Método |
|----------|-------|--------|
| `a + b` | `Add` | `add(self, rhs)` |
| `a - b` | `Sub` | `sub(self, rhs)` |
| `a * b` | `Mul` | `mul(self, rhs)` |
| `a / b` | `Div` | `div(self, rhs)` |
| `-a` | `Neg` | `neg(self)` |
| `a[i]` | `Index` | `index(&self, idx)` |
| `a[i] = v` | `IndexMut` | `index_mut(&mut self, idx)` |

---

## Definição dos Traits Principais

```rust
// std::ops::Add
pub trait Add<Rhs = Self> {
    type Output;
    fn add(self, rhs: Rhs) -> Self::Output;
}

// std::ops::Sub
pub trait Sub<Rhs = Self> {
    type Output;
    fn sub(self, rhs: Rhs) -> Self::Output;
}

// std::ops::Mul
pub trait Mul<Rhs = Self> {
    type Output;
    fn mul(self, rhs: Rhs) -> Self::Output;
}

// std::ops::Neg (operador unário -)
pub trait Neg {
    type Output;
    fn neg(self) -> Self::Output;
}

// std::ops::Index
pub trait Index<Idx: ?Sized> {
    type Output: ?Sized;
    fn index(&self, index: Idx) -> &Self::Output;
}

// std::ops::IndexMut
pub trait IndexMut<Idx: ?Sized>: Index<Idx> {
    fn index_mut(&mut self, index: Idx) -> &mut Self::Output;
}
```

Note o tipo associado `Output` — ele permite que a operação retorne um tipo diferente dos operandos. Por exemplo, multiplicar uma `Matriz` por um `Vetor` pode retornar um `Vetor`.

---

## Exemplos Práticos

### Exemplo 1: Add e Sub para um tipo Vetor2D

```rust
use std::ops::{Add, Sub, Neg};
use std::fmt;

#[derive(Debug, Clone, Copy, PartialEq)]
struct Vec2 {
    x: f64,
    y: f64,
}

impl Vec2 {
    fn new(x: f64, y: f64) -> Self {
        Vec2 { x, y }
    }

    fn magnitude(&self) -> f64 {
        (self.x * self.x + self.y * self.y).sqrt()
    }
}

impl Add for Vec2 {
    type Output = Vec2;

    fn add(self, rhs: Vec2) -> Vec2 {
        Vec2 {
            x: self.x + rhs.x,
            y: self.y + rhs.y,
        }
    }
}

impl Sub for Vec2 {
    type Output = Vec2;

    fn sub(self, rhs: Vec2) -> Vec2 {
        Vec2 {
            x: self.x - rhs.x,
            y: self.y - rhs.y,
        }
    }
}

impl Neg for Vec2 {
    type Output = Vec2;

    fn neg(self) -> Vec2 {
        Vec2 {
            x: -self.x,
            y: -self.y,
        }
    }
}

impl fmt::Display for Vec2 {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "({:.1}, {:.1})", self.x, self.y)
    }
}

fn main() {
    let a = Vec2::new(3.0, 4.0);
    let b = Vec2::new(1.0, 2.0);

    println!("a + b = {}", a + b);     // (4.0, 6.0)
    println!("a - b = {}", a - b);     // (2.0, 2.0)
    println!("-a = {}", -a);           // (-3.0, -4.0)
    println!("|a| = {:.2}", a.magnitude()); // 5.00
}
```

### Exemplo 2: Mul com tipo diferente (escalar * vetor)

```rust
use std::ops::Mul;

#[derive(Debug, Clone, Copy)]
struct Vec2 {
    x: f64,
    y: f64,
}

// Vec2 * f64 (vetor vezes escalar)
impl Mul<f64> for Vec2 {
    type Output = Vec2;

    fn mul(self, escalar: f64) -> Vec2 {
        Vec2 {
            x: self.x * escalar,
            y: self.y * escalar,
        }
    }
}

// f64 * Vec2 (escalar vezes vetor)
impl Mul<Vec2> for f64 {
    type Output = Vec2;

    fn mul(self, vec: Vec2) -> Vec2 {
        Vec2 {
            x: self * vec.x,
            y: self * vec.y,
        }
    }
}

fn main() {
    let v = Vec2 { x: 3.0, y: 4.0 };

    let dobro = v * 2.0;
    println!("{:?}", dobro);  // Vec2 { x: 6.0, y: 8.0 }

    let triplo = 3.0 * v;
    println!("{:?}", triplo); // Vec2 { x: 9.0, y: 12.0 }
}
```

### Exemplo 3: Index e IndexMut para container customizado

```rust
use std::ops::{Index, IndexMut};

#[derive(Debug)]
struct Matriz {
    dados: Vec<Vec<f64>>,
    linhas: usize,
    colunas: usize,
}

impl Matriz {
    fn nova(linhas: usize, colunas: usize) -> Self {
        Matriz {
            dados: vec![vec![0.0; colunas]; linhas],
            linhas,
            colunas,
        }
    }
}

// Acesso com matrix[(linha, coluna)]
impl Index<(usize, usize)> for Matriz {
    type Output = f64;

    fn index(&self, (linha, coluna): (usize, usize)) -> &f64 {
        &self.dados[linha][coluna]
    }
}

impl IndexMut<(usize, usize)> for Matriz {
    fn index_mut(&mut self, (linha, coluna): (usize, usize)) -> &mut f64 {
        &mut self.dados[linha][coluna]
    }
}

// Acesso a uma linha inteira com matrix[linha]
impl Index<usize> for Matriz {
    type Output = Vec<f64>;

    fn index(&self, linha: usize) -> &Vec<f64> {
        &self.dados[linha]
    }
}

fn main() {
    let mut m = Matriz::nova(3, 3);

    // IndexMut: atribuir valor
    m[(0, 0)] = 1.0;
    m[(1, 1)] = 1.0;
    m[(2, 2)] = 1.0;

    // Index: ler valor
    println!("m[1][1] = {}", m[(1, 1)]); // 1.0
    println!("m[0][1] = {}", m[(0, 1)]); // 0.0

    // Acessar uma linha inteira
    println!("Linha 0: {:?}", m[0]); // [1.0, 0.0, 0.0]
}
```

### Exemplo 4: Newtype pattern com sobrecarga de operadores

O newtype pattern encapsula um tipo existente para dar-lhe nova semântica e operadores personalizados:

```rust
use std::ops::{Add, Sub};
use std::fmt;

#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
struct Reais(f64);

#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
struct Dolares(f64);

impl Add for Reais {
    type Output = Reais;
    fn add(self, rhs: Reais) -> Reais {
        Reais(self.0 + rhs.0)
    }
}

impl Sub for Reais {
    type Output = Reais;
    fn sub(self, rhs: Reais) -> Reais {
        Reais(self.0 - rhs.0)
    }
}

impl Add for Dolares {
    type Output = Dolares;
    fn add(self, rhs: Dolares) -> Dolares {
        Dolares(self.0 + rhs.0)
    }
}

impl fmt::Display for Reais {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "R$ {:.2}", self.0)
    }
}

impl fmt::Display for Dolares {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "$ {:.2}", self.0)
    }
}

fn main() {
    let preco = Reais(100.0);
    let desconto = Reais(15.50);
    let total = preco - desconto;
    println!("Total: {}", total); // Total: R$ 84.50

    let usd1 = Dolares(50.0);
    let usd2 = Dolares(25.0);
    println!("Soma: {}", usd1 + usd2); // Soma: $ 75.00

    // ERRO DE COMPILAÇÃO: não pode somar Reais com Dolares!
    // let mistura = preco + usd1;
    // Isso é segurança de tipos em ação!
}
```

### Exemplo 5: Traits de atribuição composta (AddAssign, MulAssign)

```rust
use std::ops::{Add, AddAssign, MulAssign};

#[derive(Debug, Clone, Copy)]
struct Contador {
    valor: i64,
}

impl Contador {
    fn novo(valor: i64) -> Self {
        Contador { valor }
    }
}

impl Add for Contador {
    type Output = Contador;
    fn add(self, rhs: Contador) -> Contador {
        Contador { valor: self.valor + rhs.valor }
    }
}

// += operador
impl AddAssign for Contador {
    fn add_assign(&mut self, rhs: Contador) {
        self.valor += rhs.valor;
    }
}

// += com i64
impl AddAssign<i64> for Contador {
    fn add_assign(&mut self, rhs: i64) {
        self.valor += rhs;
    }
}

// *= com i64
impl MulAssign<i64> for Contador {
    fn mul_assign(&mut self, rhs: i64) {
        self.valor *= rhs;
    }
}

fn main() {
    let mut c = Contador::novo(10);

    c += Contador::novo(5);
    println!("{:?}", c);  // Contador { valor: 15 }

    c += 3;
    println!("{:?}", c);  // Contador { valor: 18 }

    c *= 2;
    println!("{:?}", c);  // Contador { valor: 36 }
}
```

---

## O Contrato dos Traits de Operadores

Ao implementar traits de operadores, respeite as expectativas matemáticas:

1. **Comutatividade** (quando aplicável): `a + b == b + a`
2. **Associatividade**: `(a + b) + c == a + (b + c)`
3. **Elemento neutro**: `a + zero == a`, `a * um == a`
4. **Consistência com AddAssign**: Se você implementa `Add` e `AddAssign`, eles devem produzir o mesmo resultado.

Essas propriedades não são verificadas pelo compilador, mas violá-las causará surpresas para os usuários do seu tipo.

---

## Padrões e Boas Práticas

1. **Implemente para referências também**: Para tipos que não são `Copy`, implemente os traits para `&T` além de `T`, evitando moves desnecessários:
   ```rust
   impl Add for &Vec2 {
       type Output = Vec2;
       fn add(self, rhs: &Vec2) -> Vec2 {
           Vec2 { x: self.x + rhs.x, y: self.y + rhs.y }
       }
   }
   ```

2. **Use o newtype pattern para segurança de tipos**: Encapsule tipos primitivos para evitar erros como somar metros com segundos ou reais com dólares.

3. **Output pode ser diferente de Self**: Multiplicar `Metros` por `Metros` pode retornar `MetrosQuadrados`. Use `type Output` para expressar isso.

4. **Não abuse da sobrecarga**: Operadores devem ter significado intuitivo. Não use `+` para concatenar listas se isso não for óbvio. Se a operação não é clara, use um método nomeado.

5. **Combine com Deref para smart pointers**: Para tipos wrapper, combine `Deref` com traits de operadores para criar abstrações transparentes. Veja [Deref e DerefMut](/stdlib/deref/).

6. **AddAssign separado de Add**: Implemente `AddAssign` quando `+=` faz sentido. Ele pode ser mais eficiente que `Add` por modificar in-place.

---

## Veja Também

- [Deref e DerefMut](/stdlib/deref/) — Deref coercion e smart pointers
- [Display e Debug](/stdlib/display-debug/) — frequentemente implementados junto com ops traits
- [From e Into](/stdlib/from-into/) — conversão entre tipos, útil com newtype pattern
- [Eq e Ord](/stdlib/eq-ord/) — traits de comparação para tipos com operadores
- [Erro E0369: Operador Não Aplicável](/erros/e0369-operador-nao-aplicavel/) — quando falta a implementação do trait
