Caso de prueba: aclaración de la unidad

Se puede examinar un método útil de conversión de unidades implementando Add con un parámetro de tipo fantasma. El trait Add se examina a continuación:

// Esta construcción impondría: `Self + RHS = Output` donde RHS toma el valor 
// predeterminado Self si no se especifica en la implementación.
pub trait Add<RHS = Self> {
    type Output;

    fn add(self, rhs: RHS) -> Self::Output;
}

// `Output` debe ser `T<U>` para que `T<U> + T<U> = T<U>`.
impl<U> Add for T<U> {
    type Output = T<U>;
    ...
}

Toda la implementación:

use std::ops::Add;
use std::marker::PhantomData;

/// Crea enumeraciones vacías para definir tipos de unidades.
#[derive(Debug, Clone, Copy)]
enum Pulgada {}
#[derive(Debug, Clone, Copy)]
enum Milimetro {}

/// `Longitud` es un tipo con el parámetro de tipo fantasma` Unit`,
/// y no es genérico sobre el tipo de longitud (es decir, `f64`).
///
/// `f64` ya implementa los rasgos `Clone` y `Copy`.
#[derive(Debug, Clone, Copy)]
struct Longitud<Unit>(f64, PhantomData<Unit>);

/// El rasgo `Add` define el comportamiento del operador `+`.
impl<Unit> Add for Longitud<Unit> {
     type Output = Longitud<Unit>;

    // add() devuelve una nueva estructura `Longitud` que contiene la suma.
    fn add(self, rhs: Longitud<Unit>) -> Longitud<Unit> {
      // `+` llama a la implementación de `Add` para `f64`.
        Longitud(self.0 + rhs.0, PhantomData)
    }
}

fn main() {
    // Especifica `un_pie` para tener el parámetro de tipo fantasma `Pulgada`.
    let un_pie:  Longitud<Pulgada> = Longitud(12.0, PhantomData);
    // `un_metro` tiene el parámetro de tipo fantasma `Milimetro`.
    let un_metro: Longitud<Milimetro>   = Longitud(1000.0, PhantomData);

    // `+` llama al método `add()` que implementamos para `Longitud<Unit>`.
    //
    // Dado que `Longitud` implementa `Copy`, `add()` no consume `un_pie` y `un_metro`
    // pero los copia en `self` y `rhs`.
    let dos_pies = un_pie + un_pie;
    let dos_metros = un_metro + un_metro;

    // Agregarition works.
    println!("un pie + un_pie = {:?} pulgadas", dos_pies.0);
    println!("un metro + un_metro = {:?} milimetros", dos_metros.0);

    // Las operaciones sin sentido fallan como deberían:
    // Error en tiempo de compilación: no coinciden los tipos.
    //let un_feter = un_pie + un_metro;
}

Ve también

Préstamo (&), Vínculos (X: Y), enum, impl & self, Sobrecarga, ref, Rasgos (X for Y) y Estructuras.