Uso del rasgo de derivación

Completado

Es posible que haya observado que nuestros tipos personalizados resultan un poco difíciles de usar en la práctica. Esta estructura Point simple no puede compararse con otras instancias de Point ni mostrarse en el terminal. Debido a esta dificultad, es posible que queramos usar el atributo derive para permitir que se generen automáticamente nuevos elementos para la estructura.

Inconvenientes de los tipos genéricos

Eche un vistazo al ejemplo de código siguiente:

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!

}

Se producirá un error en el código anterior por tres motivos. Vea esta salida:

    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

Este código no se compila porque el tipo Point no implementa los rasgos siguientes:

  • El rasgo Debug, que permite dar formato a un tipo mediante el especificador de formato {:?}, se usa en un contexto de depuración orientado al programador.
  • El rasgo Display, que permite dar formato a un tipo mediante el especificador de formato {}, es similar a Debug. Pero Display es más adecuado para la salida orientada al usuario.
  • El rasgo PartialEq, que permite comparar la igualdad de los implementadores.

Uso de derivación

Afortunadamente, el compilador de Rust puede implementar automáticamente los rasgos Debug y PartialEq mediante el atributo #[derive(Trait)] si cada uno de sus campos implementa el rasgo:

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

Nuestro código seguirá sin compilarse porque la biblioteca estándar de Rust no ofrece la implementación automática del rasgo Display, porque está pensado para los usuarios finales. Pero si esa línea se convierte en comentario, el código genera esta salida:

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

No obstante, podemos implementar el rasgo Display para nuestro tipo por nosotros mismos:

use std::fmt;

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

Ahora se compilará el código:

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

Eche un vistazo al código de este ejemplo en este vínculo al área de juegos de Rust.