< Structs<  |    |  Crates & Modules >>

Enums und Pattern Matching

Enums

Enumarations gibt’s in vielen Programmiersprachen, in Rust scheinen sie aber eine große Rolle einzunehmen. "Enumeration" stimmt eigentlich gar nicht, Enums haben hier nämlich nicht zwangsläufig was mit Zahlen zu tun. Grundsätzlich ist ein "Enum" in Rust näher am "Union" würde ich denken.

Ein einfaches Beispiel für ist der Typ Option<T> (vergleichbar mit Python oder Java Optional). Dieser ist entweder None oder Some(value: T) - es kann also ein Wert zusätzlich zur "Definition" beinhalten.

enum Farbcode {
    Hex,
    Rgb,
}

let hexcolor = Farbcode::Hex;

Farbcode ist also ein im Code benutzbarer Datentyp, genauso wie Farbcode::Hex. Wenn eine Funktion nun eine Variable mit Typ Farbcode erwartet, kann diese Variable sowohl Hex oder Rgb sein. Die Funktion kann dann je nach Typ verschieden funktionieren.

Wie schon erwähnt, kann so ein Enum-Wert auch Werte beinhalten, um das zu machen, schreiben wir den Code einfach um:

enum Farbcode {
    Hex(String),
    Rgb(u8, u8, u8),
}

// Alternativ:
struct Hex(String);
struct Rgb(u8, u8, u8);

enum Farbcode {
    Hex,
    Rgb
}

let hexcode = Farbcode::Hex(String::from("00affe"));
let rgbcode = Farbcode::Rgb(125, 255, 255);

Natürlich können die Structs jeder Art sein. Enums sind aber auch selber eine Art Struct. Also können wir für Enums auch Methoden definieren wie für Structs.

impl Farbcode {
    fn to_css_string(&self) {
        // Methode, die für Hex und Rgb angewendet werden kann
    }
}

let rgbcode = Farbcode::Rgb(125, 255, 255);
rgbcode.to_css_string();

Tatsächlich ist damit so etwas wie Vererbung implementierbar. Es gibt zwar keine Attribute, aber da ja auch die internen Structs Methoden haben können, ist eine gewisse Hierarchie erstellbar.

Option<T>

Options hab ich oben schonmal kurz beschrieben. In Rust ist dieser Datentyp sehr wichtig. Die Dokumentation dazu ist hier zu finden und enthält sehr viel Wichtiges und Interessantes.

match

match ist quasi das switch von Rust. Nur kann es auch prüfen, ob eine Variable einem Enum-Typen angehört. So wie Rust bis jetzt klang, kann wahrscheinlich jedem Datentypen ein "match-Trait" gegeben werden, der dann eine "Zugehörigkeit" (Gleichheit stimmt ja irgendwie nicht) prüfen kann.

Aber ganz einfach: Angenommen wir wollen die Methode to_css_string von oben implementieren. Diese Methode muss ja, je nach Typ, völlig unterschiedlich funktionieren.

enum Farbcode {
    Hex(String),
    Rgb(u8, u8, u8),
}

impl Farbcode {
    fn to_css_string(&self) -> String {
        match self {
            // format! ist offensichtlich ein Pragma, dass Strings erstellt auf die selbe Weise wie println!
            Farbcode::Hex(hex) => format!("#{}", hex),
            Farbcode::Rgb(r, g, b) => format!("rgb({}, {}, {})", r, g, b),
        }
    }
}

fn main() {
    let hexcode = Farbcode::Hex(String::from("affe00"));
    let rgbcode = Farbcode::Rgb(125, 255, 255);

    println!("{}", hexcode.to_css_string());
    println!("{}", rgbcode.to_css_string());
}

Hier sieht man auch ganz gut, wie im Match dem "Inhalt" des Enums direkt Namen gegeben werden und Tuples auch dekonstruiert. Im Beispiel ist auch deutlich, dass match einen Rückgabewert hat, nämlich das, was im Statement(-Block) des jeweiligen Matches zurückgegeben wird.

Vollständigkeit

Entweder muss ein match eines Enums jede mögliche Variante abgrasen oder es gibt zwei Alternativen.

other ist quasi das default von Rust. Aber auch _ matched alles. Der Unterschied ist, dass bei other noch der Inhalt genutzt werden kann, bei _ wird er direkt ignoriert und ist nicht nutzbar.

if let

Dieses if-Konstrukt nutzt man am besten, wenn man nur auf eine einzelne Variante eines Enums prüfen möchte. Letztendlich ist es ganz simpel:

#[derive(Debug)]
enum Muenzwurf {
    Kopf,
    Zahl,
    Seite
}

fn print_wurf(ergebnis: Muenzwurf) {
    if let Muenzwurf::Seite = ergebnis {
        println!("Das glaub ich nicht! Seite?!");
    } else {
        println!("Du hast {:?} geworfen.", ergebnis);
    }
}

fn main() {
    let ergebnis = Muenzwurf::Zahl;
    print_wurf(ergebnis); // Du hast Zahl geworfen.
    let ergebnis = Muenzwurf::Seite;
    print_wurf(ergebnis); // Das glaub ich nicht! Seite?!
}

< Structs<  |    |  Crates & Modules >>