Traits esenciales de la stdlib de Rust: Display, Debug, From, Into, Iterator

La stdlib de Rust define decenas de traits, pero hay un conjunto reducido que aparece en casi todo el código. Implementarlos correctamente hace que tus tipos se integren con el ecosistema: se pueden imprimir, comparar, ordenar, convertir y usar como claves en mapas. Esta guía cubre los más importantes con ejemplos y errores reales del compilador.

Display: formato para el usuario

use std::fmt;

struct Punto { x: f64, y: f64 }

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

fn main() {
    let p = Punto { x: 1.0, y: 2.0 };
    println!("{}", p);      // (1, 2)
    let s = p.to_string();  // to_string() se implementa automáticamente
    println!("{}", s);
}

Debug: formato para el programador

#[derive(Debug)]
struct Punto { x: f64, y: f64 }

fn main() {
    let p = Punto { x: 1.0, y: 2.0 };
    println!("{:?}", p);   // Punto { x: 1.0, y: 2.0 }
    println!("{:#?}", p);  // Pretty-printed
}

Casi siempre basta con #[derive(Debug)]. Solo implementas manualmente si quieres controlar el formato de depuración.

From e Into: conversiones sin pérdida

struct Email(String);

impl From<String> for Email {
    fn from(s: String) -> Self {
        Email(s)
    }
}

fn main() {
    let e1 = Email::from(String::from("[email protected]"));

    // Into se implementa automáticamente cuando implementas From
    let e2: Email = String::from("[email protected]").into();

    println!("{}", e1.0);
    println!("{}", e2.0);
}

La regla: implementa siempre From, no Into directamente. Into se genera automáticamente a partir de From.

PartialEq y Eq: igualdad

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

fn main() {
    let a = Punto { x: 1.0, y: 2.0 };
    let b = Punto { x: 1.0, y: 2.0 };
    println!("{}", a == b); // true
    println!("{}", a != b); // false
}

PartialEq permite usar == y !=. Eq (que extiende PartialEq) indica que la igualdad es total (reflexiva, simétrica, transitiva). f64 implementa PartialEq pero no Eq porque NaN != NaN.

PartialOrd y Ord: ordenación

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
struct Version {
    mayor: u32,
    menor: u32,
    patch: u32,
}

fn main() {
    let mut versiones = vec![
        Version { mayor: 1, menor: 2, patch: 0 },
        Version { mayor: 2, menor: 0, patch: 0 },
        Version { mayor: 1, menor: 0, patch: 5 },
    ];
    versiones.sort();
    println!("{:?}", versiones);
    // Ordena: 1.0.5, 1.2.0, 2.0.0
}

Default: valores iniciales

#[derive(Default, Debug)]
struct Config {
    debug: bool,       // false
    nivel: u32,        // 0
    nombre: String,    // ""
    timeout: u64,      // 0
}

fn main() {
    let cfg = Config::default();
    println!("{:?}", cfg);

    // Usar ..Default::default() con struct update syntax
    let cfg2 = Config {
        debug: true,
        nombre: String::from("app"),
        ..Default::default()
    };
    println!("{:?}", cfg2);
}

Resumen de errores comunes

// Error: no implementa Display
struct MiTipo;
println!("{}", MiTipo); // error[E0277]: `MiTipo` doesn't implement `std::fmt::Display`

// Solución: implementar Display o usar Debug con {:?}
#[derive(Debug)]
struct MiTipo;
println!("{:?}", MiTipo);

Resumen

  • Display: formato legible por el usuario. Se usa con {}.
  • Debug: formato de depuración. Se usa con {:?}. Casi siempre derive.
  • From<T>: conversión desde otro tipo. Into<T> se genera automáticamente.
  • PartialEq / Eq: igualdad con == y !=.
  • PartialOrd / Ord: ordenación con <, >, sort().
  • Default: valor por defecto con Type::default().

El siguiente artículo explora los closures: funciones anónimas que capturan el entorno, los tres modos de captura y los traits Fn, FnMut y FnOnce.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP