Anpassen der Rust-Strukturausgabe mit fmt::Display

Beginner

This tutorial is from open-source community. Access the source code

Einführung

In diesem Lab lernst du, wie du das fmt::Display-Trait in Rust implementierst, um das Ausgabeverhalten einer Struktur anzupassen. Du wirst auch untersuchen, was das Unterschied zwischen fmt::Display und fmt::Debug ist, sowie die Einschränkungen von fmt::Display für generische Containertypen. Schließlich wirst du eine Aktivität durchführen, um das fmt::Display-Trait für eine neue Complex-Struktur zu implementieren und sie in einem bestimmten Format auszugeben.

Hinweis: Wenn das Lab keinen Dateinamen angibt, kannst du einen beliebigen Dateinamen verwenden. Beispielsweise kannst du main.rs verwenden und es mit rustc main.rs &&./main kompilieren und ausführen.

Display

fmt::Debug sieht kaum kompakt und sauber aus, daher ist es oft vorteilhaft, das Ausgabeverhalten anzupassen. Dies wird durch die manuelle Implementierung von fmt::Display erreicht, das das {}-Ausgabemarkierungszeichen verwendet. Die Implementierung sieht so aus:

// Importiere (über `use`) das `fmt`-Modul, um es verfügbar zu machen.
use std::fmt;

// Definiere eine Struktur, für die `fmt::Display` implementiert werden soll. Dies ist
// eine Tuple-Struktur namens `Structure`, die eine `i32` enthält.
struct Structure(i32);

// Um das `{}`-Zeichen zu verwenden, muss das `fmt::Display`-Trait
// manuell für den Typ implementiert werden.
impl fmt::Display for Structure {
    // Dieses Trait erfordert `fmt` mit dieser genauen Signatur.
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Schreibe genau das erste Element in den bereitgestellten Ausgabestream: `f`. Gibt `fmt::Result` zurück, das angibt, ob die Operation erfolgreich war oder fehlgeschlagen ist. Beachte, dass `write!` eine Syntax verwendet, die sehr ähnlich zu `println!` ist.
        write!(f, "{}", self.0)
    }
}

fmt::Display kann möglicherweise sauberer aussehen als fmt::Debug, aber dies stellt ein Problem für die std-Bibliothek dar. Wie sollten ambiguöse Typen dargestellt werden? Beispielsweise, wenn die std-Bibliothek eine einheitliche Darstellung für alle Vec<T> implementierte, welche Darstellung sollte es sein? Würde es eine dieser beiden sein?

  • Vec<path>: /:/etc:/home/username:/bin (getrennt durch :)
  • Vec<number>: 1,2,3 (getrennt durch ,)

Nein, denn es gibt keine ideale Darstellung für alle Typen und die std-Bibliothek macht keine Prätention, eine festzulegen. fmt::Display ist für Vec<T> oder für andere generische Container nicht implementiert. Für diese generischen Fälle muss daher fmt::Debug verwendet werden.

Dies ist jedoch kein Problem, da für jede neue Container-Struktur, die nicht generisch ist, fmt::Display implementiert werden kann.

use std::fmt; // Importiere `fmt`

// Eine Struktur, die zwei Zahlen enthält. `Debug` wird abgeleitet, damit die Ergebnisse
// mit `Display` verglichen werden können.
#[derive(Debug)]
struct MinMax(i64, i64);

// Implementiere `Display` für `MinMax`.
impl fmt::Display for MinMax {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Verwende `self.number`, um auf jedes positionale Datenpunkt zu verweisen.
        write!(f, "({}, {})", self.0, self.1)
    }
}

// Definiere eine Struktur, bei der die Felder benannt werden können, um einen Vergleich zu ermöglichen.
#[derive(Debug)]
struct Point2D {
    x: f64,
    y: f64,
}

// Entsprechend implementiere `Display` für `Point2D`.
impl fmt::Display for Point2D {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Anpassen, sodass nur `x` und `y` angegeben werden.
        write!(f, "x: {}, y: {}", self.x, self.y)
    }
}

fn main() {
    let minmax = MinMax(0, 14);

    println!("Vergleiche Strukturen:");
    println!("Display: {}", minmax);
    println!("Debug: {:?}", minmax);

    let big_range =   MinMax(-300, 300);
    let small_range = MinMax(-3, 3);

    println!("Der große Bereich ist {big} und der kleine ist {small}",
             small = small_range,
             big = big_range);

    let point = Point2D { x: 3.3, y: 7.2 };

    println!("Vergleiche Punkte:");
    println!("Display: {}", point);
    println!("Debug: {:?}", point);

    // Fehler. Sowohl `Debug` als auch `Display` wurden implementiert, aber `{:b}`
    // erfordert die Implementierung von `fmt::Binary`. Dies wird nicht funktionieren.
    // println!("Wie sieht Point2D in binär aus: {:b}?", point);
}

Somit wurde fmt::Display implementiert, aber fmt::Binary nicht und kann daher nicht verwendet werden. std::fmt hat viele solche Traits, und jedes erfordert seine eigene Implementierung. Dies wird in std::fmt weiter ausgeführt.

Aktivität

Nachdem du die Ausgabe des obigen Beispiels überprüft hast, verwende die Point2D-Struktur als Leitfaden, um der Beispiel eine Complex-Struktur hinzuzufügen. Wenn sie auf die gleiche Weise gedruckt wird, sollte die Ausgabe wie folgt sein:

Display: 3.3 + 7.2i
Debug: Complex { real: 3.3, imag: 7.2 }

Zusammenfassung

Herzlichen Glückwunsch! Du hast das Display-Lab abgeschlossen. Du kannst in LabEx weitere Labs absolvieren, um deine Fähigkeiten zu verbessern.