0 Tage Serie
rust / expert
Snippet

Kompilierzeit-Erzwingung von API-Zuständen durch das Typestate-Muster

Dieses Snippet implementiert das Typestate-Muster, welches das Typsystem von Rust nutzt, um gültige Zustandsübergänge eines Objekts zur Kompilierzeit zu erzwingen. Durch den Verbrauch von `self` in Übergangsmethoden (wie `connect`) garantieren wir, dass veraltete Zustände nicht wiederverwendet werden können, was logische Fehler zur Laufzeit ausschließt.

snippet.rs
rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
pub struct Uninitialized;
pub struct Connected {
pub socket_id: u32,
}
pub struct Connection<State> {
pub host: String,
pub state: State,
}
impl Connection<Uninitialized> {
pub fn new(host: &str) -> Self {
Self {
host: host.to_string(),
state: Uninitialized,
}
}
pub fn connect(self) -> Connection<Connected> {
Connection {
host: self.host,
state: Connected { socket_id: 101 },
}
}
}
impl Connection<Connected> {
pub fn send(&self, data: &[u8]) {
println!("Sending to socket {}: {:?}", self.state.socket_id, data);
}
}
Erklärung
1
pub struct Connection<State>
Deklariert einen generischen Verbindungstyp, der durch seinen aktuellen Lebenszyklus-Zustand parametrisiert wird.
2
impl Connection<Uninitialized>
Implementiert Funktionen, die nur gültig sind, wenn die Verbindung im nicht initialisierten Zustand ist.
3
pub fn connect(self) -> Connection<Connected>
Konsumiert den Besitz von `self` (dem uninitialized Zustand) und gibt eine neue Verbindung im connected Zustand zurück.
4
impl Connection<Connected>
Implementiert Funktionen (wie `send`), die ausschließlich verfügbar sind, nachdem eine Verbindung erfolgreich hergestellt wurde.
5
self.state.socket_id
Greift auf Daten innerhalb der `Connected`-Zustandsstruktur zu, die nur in diesem Zustand existieren.