Impresión para mostrar
fmt::Debug
apenas parece compacto y limpio, por lo que a menudo es ventajoso
personalizar la apariencia de salida. Esto se hace implementando manualmente
fmt::Display
, que usa el marcador de impresión {}
. Implementarlo se
ve así:
#![allow(unused)] fn main() { // Importa (a través de `use`) el módulo `fmt` para que esté disponible. use std::fmt; // Define una estructura para la cual se implementará `fmt::Display`. Esta es // una estructura de tupla llamada `Estructura` que contiene un `i32`. struct Estructura(i32); // Para usar el marcador `{}`, se debe implementar el rasgo `fmt::Display` // manualmente para el tipo. impl fmt::Display for Estructura { // Este rasgo requiere `fmt` con esta firma exacta. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // Escribe estrictamente el primer elemento en la secuencia de salida // proporcionada: `f`. Devuelve `fmt::Result` que indica si la // operación es exitosa o fallida. Ten en cuenta que `write!` usa una // sintaxis que es muy similar a `println!`. write!(f, "{}", self.0) } } }
fmt::Display
puede ser más limpio que fmt::Debug
pero esto presenta un
problema para la biblioteca std
. ¿Cómo se deben mostrar los tipos ambiguos?
Por ejemplo, si la biblioteca std
implementó un solo estilo para todos los
Vec<T>
, ¿qué estilo debería ser? ¿Sería alguno de estos dos?
Vec <ruta>
:/:/etc:/home/usuario:/bin
(dividido en:
)Vec <número>
:1,2,3
(dividido en,
)
No, porque no existe un estilo ideal para todos los tipos y la biblioteca std
no pretende imponer uno. fmt::Display
no está implementado para Vec<T>
ni
para ningún otro contenedor genérico. fmt::Debug
debe usarse entonces para
estos casos genéricos.
Sin embargo, esto no es un problema porque para cualquier nuevo tipo
contenedor que no sea genérico, se puede implementar fmt::Display
.
use std::fmt; // Importa `fmt` // Una estructura con dos números. `Debug` se derivará para que los resultados // puedan contrastarse con `Display`. #[derive(Debug)] struct MinMax(i64, i64); // Implementa `Display` para `MinMax`. impl fmt::Display for MinMax { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // Utiliza `self.number` para referirse a cada punto de datos posicionales. write!(f, "({}, {})", self.0, self.1) } } // Define una estructura en la que los campos se puedan nombrar para comparar. #[derive(Debug)] struct Punto2D { x: f64, y: f64, } // De manera similar, implementa `Display` para `Punto2D` impl fmt::Display for Punto2D { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // Personalice de modo que solo se denoten `x` y `y`. write!(f, "x: {}, y: {}", self.x, self.y) } } fn main() { let minmax = MinMax(0, 14); println!("Compara estructuras:"); println!("Para mostrar: {}", minmax); println!("Para depurar: {:?}", minmax); let rango_grande = MinMax(-300, 300); let rango_pequeno = MinMax(-3, 3); println!("El rango grande es {grande} y el pequeño es {pequeño}", pequeño = rango_pequeno, grande = rango_grande); let punto = Punto2D { x: 3.3, y: 7.2 }; println!("Compara puntos:"); println!("Para mostrar: {}", punto); println!("Para depurar: {:?}", punto); // Error. Se implementaron tanto `Debug` como` Display`, pero `{:b}` // requiere que se implemente `fmt::Binary`. Esto no funcionará. // println!("¿Cómo se ve Punto2D en binario: {:b}?", punto); }
Entonces, fmt::Display
se ha implementado pero fmt::Binary
no, y por lo
tanto no se puede usar. std :: fmt
tiene muchos de esos rasgos
se detalla más en std::fmt
.
Actividades
Después de verificar el resultado del ejemplo anterior, use la estructura
Punto2D
como guía para agregar una estructura compleja al ejemplo. Cuando se
imprime de la misma manera, la salida debe ser:
Para mostrar: 3.3 + 7.2i
Para depurar: Complex { real: 3.3, imag: 7.2 }