Menu
Coddy logo textTech

Newtype-Pattern

Teil des Abschnitts Objektorientierte Programmierung der Rust-Journey von Coddy. Lektion 54 von 61.

Manchmal hast du zwei Werte desselben Typs, die völlig unterschiedliche Dinge darstellen. Die E-Mail-Adresse und das Passwort eines Benutzers könnten beide String-Werte sein, aber sie versehentlich in einem Funktionsaufruf zu vertauschen, wäre ein schwerwiegender Fehler. Das Newtype-Muster löst dieses Problem, indem es verschiedene Typen erstellt, die primitive Datentypen umschließen.

Ein Newtype ist einfach eine Tupelstruktur mit einem einzigen Feld:

struct Password(String);
struct Email(String);

Obwohl beide einen String umschließen, behandelt der Compiler sie als vollständig unterschiedliche Typen. Du kannst nicht versehentlich ein Password übergeben, wo ein Email erwartet wird: Der Compiler erkennt den Fehler.

fn send_reset_email(email: Email, password: Password) {
    // ...
}

let email = Email(String::from("user@example.com"));
let pass = Password(String::from("secret123"));

send_reset_email(email, pass);  // Korrekte Reihenfolge durch Typen erzwungen
// send_reset_email(pass, email);  // Kompiliert nicht!

Um auf den inneren Wert zuzugreifen, verwende .0, da es sich um eine Tupelstruktur handelt:

let pass = Password(String::from("secret123"));
println!("Length: {}", pass.0.len());

Du kannst deinem Newtype auch Methoden hinzufügen und ihm damit ein Verhalten geben, das speziell zu dem passt, was es repräsentiert, etwa Validierung oder Formatierung, was bei einem einfachen String keinen Sinn ergeben würde.

challenge icon

Aufgabe

Einfach

Erstelle ein typsicheres Benutzerregistrierungssystem mithilfe des Newtype-Musters! Du erstellst unterschiedliche Typen für Username und UserId, um zu verhindern, dass sie versehentlich verwechselt werden, obwohl beide einfache Werte kapseln.

Du organisierst deinen Code über zwei Dateien:

  • user_types.rs: Definiere zwei öffentliche Newtype-Strukturen: Username, die einen String kapselt, und UserId, die einen u32 kapselt. Füge jeder Struktur eine Methode namens value hinzu, die eine Referenz auf die inneren Daten zurückgibt (für Username soll &String zurückgegeben werden, für UserId &u32). Dadurch erhältst du kontrollierten Zugriff auf die gekapselten Werte.
  • main.rs: Binde dein user_types-Modul ein und erstelle eine Funktion namens display_user, die als ersten Parameter ein UserId und als zweiten Parameter einen Username entgegennimmt. Die Funktion soll die Benutzerinformationen ausgeben. Erstelle anschließend Instanzen beider Typen mithilfe der bereitgestellten Eingaben und rufe deine Funktion auf.

Die entscheidende Erkenntnis hierbei ist, dass der Compiler UserId und Username als vollständig unterschiedliche Typen behandelt, obwohl sie nur einfache Wrapper sind. Du kannst nicht versehentlich einen Benutzernamen übergeben, wenn eine ID erwartet wird!

Deine display_user-Funktion soll in diesem Format ausgeben:

User #{id}: {username}

Zum Beispiel mit den Eingaben 42 und alice_dev:

User #42: alice_dev

Und mit den Eingaben 1001 und bob_smith:

User #1001: bob_smith

Du erhältst zwei Eingaben: die Benutzer-ID (als u32 parsen) und den Benutzernamen als Zeichenkette.

Probier es selbst

mod user_types;

use user_types::{Username, UserId};

// TODO: Erstelle eine Funktion namens `display_user`, die Folgendes nimmt:
// - Erster Parameter: UserId
// - Zweiter Parameter: Username
// Die Funktion sollte ausgeben: "User #{id}: {username}"
// Verwende die .value()-Methode, um auf die inneren Werte zuzugreifen


fn main() {
    // Eingabe lesen
    let mut id_input = String::new();
    std::io::stdin().read_line(&mut id_input).expect("Failed to read line");
    let id: u32 = id_input.trim().parse().expect("Invalid number");
    
    let mut username_input = String::new();
    std::io::stdin().read_line(&mut username_input).expect("Failed to read line");
    let username = username_input.trim().to_string();
    
    // TODO: Erstelle Instanzen von UserId und Username unter Verwendung der Eingaben
    
    // TODO: Rufe display_user mit den korrekten Argumenten auf
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-Rust-Compiler