Vor gut einer Woche ist Rust 1.40 erschienen. Im heutigen Artikel gehe ich kurz auf die Hauptänderung ein.
Überblick
Das gesamte Changelog ist in der RELEASES.md einzusehen. Highlights sind das #[non_exhaustive]
Attribut sowie weitere Verbesserungen für andere Makros und Attribute wie z.B. dem neuen todo!()
oder Makros in extern-Blöcken.
Das non-exhaustive Attribut
Wie oben schon geschrieben, ist das "Highlight" des Releases die Einführung des non-exhaustive-Attributes, um Aufwärtskompatibilität sicherzustellen. Es erzwingt, dass immer ein sonst/else/default-Fall definiert werden muss, um die Fallunterscheidung auszuschöpfen. Und da das so kompliziert klingt, hier ein Beispiel:
enum DebianVersion {
Debian9,
Debian10
}
fn main() {
let deb_version = DebianVersion::Debian9;
match deb_version {
DebianVersion::Debian9 | DebianVersion::Debian10 => {
println!("Debian is used!");
}
}
}
Dieses kleine Programm besteht aus zwei wichtigen Komponenten, die für unser Beispiel wichtig sind: einem enum, das eine Reihe von Debian-Versionen definiert und einem match-Blöck, das Pattern Matching durchführt und mit einem switch-Blöck aus C vergleichbar ist. Eine Eigenschaft ist, dass ein solches Pattern Matching exhaustive sein muss: alle möglichen Fälle eines enums müssen abgedeckt sein. Das ist ein Feature, das mir an Rust besonders Spaß macht, da man im Vorhinein alle Fälle durchdenken muss.
Schwierigkeiten entstehen aber, wenn das Enum möglicherweise gar nicht in der eigenen Verantwortung liegt und durch ein Crate, eine Library, bereitgestellt wird. Wird nämlich dann ein neues EnumItem (z.B. Debian11
) hinzugefügt, bricht der match-Blöck, da dieser dann nicht mehr alle Fälle abdeckt. Auf der Implementierungsseite kann man dies i.d.R. durch den _
-Fall umgehen, der für alle anderen Fälle steht. (vgl. default
aus C) Sieht dann umgesetzt so aus:
#[non-exhaustive]
enum DebianVersion {
Debian9,
Debian10
}
fn main() {
let deb_version = DebianVersion::Debian9;
match deb_version {
DebianVersion::Debian9 | DebianVersion::Debian10 => {
println!("Debian is used!");
}
_ => {
println!("Another Debian version is used!");
}
}
}
Fällt euch was auf? Ich habe nicht nur den else/default-Fall abgdeckt, sondern auch zusätzlich das neue Attribut bereits im Quellcode untergebracht. Denn während die Implementierung jederzeit den else/default-Fall abdecken kann (oder eben auch nicht), wird das neue Attribut (jetzt noch nicht, aber bald) diesen Fall erzwingen.