Viele Systemsprachen bieten grundlegende Unterstützung für Ein- und Ausgabeoperationen, machen jedoch oft keinen klaren Unterschied zwischen synchronen und asynchronen Ansätzen. Rust wurde von Anfang an mit dem Ziel entwickelt, Sicherheit und Leistung zu gewährleisten, auch bei der Arbeit mit I/O. Daher wird in der Standardbibliothek von Rust (std) nur synchrone I/O implementiert, während die beliebte asynchrone Unterstützung in separate externe Bibliotheken (z. B. tokio, async-std) ausgelagert wird.
Die Vermischung von synchronen und asynchronen I/O-Ansätzen führt häufig zu komplizierter Wartung des Codes sowie zu Sicherheits- und Leistungsproblemen, da diese Ansätze unterschiedliche Modelle für den Umgang mit Ressourcen, Threads und Sperren haben. Beispielsweise kann das direkte Lesen einer großen Datei oder das Warten auf Daten aus dem Netzwerk den Ausführungs-Thread (inklusive dem Haupt-Thread) blockieren und die Anwendung verlangsamen.
Rust bietet eine klare Trennung: Die Standardbibliothek (std::io) — nur synchrone I/O mit sicherer Fehlerbehandlung und strikter Kontrolle über den Besitz von Ressourcen. Für asynchrone Lösungen werden externe Bibliotheken und asynchrone Runtimes verwendet — sie stellen ihre eigenen Typen und APIs zur Verfügung, jedoch mit ähnlicher Semantik für Fehler und Sicherheit.
Beispiel für synchrones Lesen einer Datei:
use std::fs::File; use std::io::{self, Read}; fn main() -> io::Result<()> { let mut file = File::open("foo.txt")?; let mut contents = String::new(); file.read_to_string(&mut contents)?; println!("{}", contents); Ok(()) }
Beispiel für asynchrones Lesen einer Datei über tokio:
use tokio::fs::File; use tokio::io::AsyncReadExt; #[tokio::main] async fn main() -> tokio::io::Result<()> { let mut file = File::open("foo.txt").await?; let mut contents = String::new(); file.read_to_string(&mut contents).await?; println!("{}", contents); Ok(()) }
Hauptmerkmale:
Kann man typengenehmigte Kombinationen (z. B. File aus std und File aus tokio) direkt verwenden, um zwischen synchronen und asynchronen Funktionen zu übergeben?
Nein. Sie sind auf API-Ebene inkompatibel, und die Standardtypen implementieren keine asynchronen Traits.
Blockiert std::thread::spawn in einer asynchronen Funktion, wenn synchrones I/O aufgerufen wird?
Ja. Wenn in einer asynchronen Umgebung synchrones I/O aufgerufen wird, wird der Thread blockiert, wodurch die Vorteile der Asynchronität zunichtegemacht werden.
Kann man async fn main ohne Runtime (tokio oder async-std) verwenden?
Nein. Der asynchrone Einstiegspunkt muss von einer speziellen Runtime ausgeführt werden, andernfalls erlaubt der Compiler nicht, async fn main zu verwenden.
In einem mehrsprachigen Server in Rust wird synchrones Lesen aus std::io in asynchronen Anfrage-Handlern verwendet. Dies führt dazu, dass die Last den Event-Loop blockiert, die Verzögerungen steigen und der Server mit Spitzenlasten nicht zurechtkommt.
Vorteile:
Nachteile:
Es werden nur asynchrone Typen für alle Datei- und Netzwerkoperationen innerhalb asynchronen Codes verwendet, wobei strengen Wert auf Typen gelegt und Fehler über Result behandelt werden.
Vorteile:
Nachteile: