Usar a característica derive

Concluído

Talvez você tenha notado que nossos tipos personalizados são um pouco difíceis de usar na prática. Esse struct Point simples não pode ser comparado com outras instâncias de Point nem exibido no terminal. Devido a essa dificuldade, podemos usar o atributo derive para permitir que novos itens sejam gerados automaticamente para o struct.

Desvantagem dos tipos genéricos

Dê uma olhada no seguinte exemplo de código:

struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let p1 = Point { x: 1, y: 2 };
    let p2 = Point { x: 4, y: -3 };

    if p1 == p2 { // can't compare two Point values!
        println!("equal!");
    } else {
        println!("not equal!");
    }

    println!("{}", p1); // can't print using the '{}' format specifier!
    println!("{:?}", p1); //  can't print using the '{:?}' format specifier!

}

O código anterior falhará por três motivos. Veja esta saída:

    error[E0277]: `Point` doesn't implement `std::fmt::Display`
      --> src/main.rs:10:20
       |
    10 |     println!("{}", p1);
       |                    ^^ `Point` cannot be formatted with the default formatter
       |
       = help: the trait `std::fmt::Display` is not implemented for `Point`
       = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
       = note: required by `std::fmt::Display::fmt`
       = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

    error[E0277]: `Point` doesn't implement `Debug`
      --> src/main.rs:11:22
       |
    11 |     println!("{:?}", p1);
       |                      ^^ `Point` cannot be formatted using `{:?}`
       |
       = help: the trait `Debug` is not implemented for `Point`
       = note: add `#[derive(Debug)]` or manually implement `Debug`
       = note: required by `std::fmt::Debug::fmt`
       = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

    error[E0369]: binary operation `==` cannot be applied to type `Point`
      --> src/main.rs:13:11
       |
    13 |     if p1 == p2 {
       |        -- ^^ -- Point
       |        |
       |        Point
       |
       = note: an implementation of `std::cmp::PartialEq` might be missing for `Point`

    error: aborting due to 3 previous errors#+end_example

Esse código não é compilado porque nosso tipo Point não implementa as seguintes características:

  • A característica Debug, que permite que um tipo seja formatado usando o especificador de formato {:?}, é usada em um contexto de depuração voltado para o programador.
  • A característica Display, que permite que um tipo seja formatado usando o especificador de formato {}, é semelhante a Debug. No entanto, Display é mais adequado à saída voltada para o usuário.
  • A característica PartialEq, que permite que os implementadores sejam comparados por igualdade.

Usar derive

Felizmente, as características Debug e PartialEq poderão ser implementadas automaticamente para nós pelo compilador Rust usando o atributo #[derive(Trait)], se cada um de seus campos implementar a característica:

#[derive(Debug, PartialEq)]
struct Point {
    x: i32,
    y: i32,
}

Nosso código ainda não será compilado porque a biblioteca padrão do Rust não fornece implementação automática para a característica Display, pois ela é destinada aos usuários finais. No entanto, se comentarmos essa linha, nosso código agora produzirá essa saída:

    not equal!
    Point { x: 1, y: 2 }

Ainda assim, podemos implementar a característica Display para nosso tipo por conta própria:

use std::fmt;

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

O código agora será compilado:

    not equal!
    (1, 2)
    Point { x: 1, y: 2 }

Confira o código deste exemplo neste link do Rust Playground.