Asynchrone Programmierung in Rust: vollständiger praktischer Leitfaden

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Einführung in die asynchrone Programmierung mit Rust



Moderne Anwendungen erfordern zunehmend hohe Leistung bei der Arbeit mit Netzwerken, Dateisystemen oder Datenbanken. Asynchrone Programmierung in Rust ermöglicht die effiziente Verarbeitung tausender gleichzeitiger Verbindungen, ohne für jede Aufgabe einen Thread zu erstellen. Im Gegensatz zur Multithreading verwendet asynchroner Code weniger Speicher und skaliert besser.



Rust bietet einen einzigartigen Ansatz für Asynchronität, der auf Abstraktionen ohne zusätzliche Kosten basiert. Das bedeutet, dass Sie nur für das bezahlen, was Sie nutzen – keine Gemeinkosten für ungenutzte Funktionen. In diesem Leitfaden werden wir die wichtigsten Konzepte erläutern und praktische Beispiele schreiben, die Ihnen helfen, noch heute asynchronen Code in Rust zu schreiben.



Grundlagen von async/await und Futures



Was ist ein Future?



Future ist ein Trait, der eine asynchrone Operation darstellt, die in der Zukunft abgeschlossen sein kann. In Rust ist Future eine faule Entität: Sie tut nichts, bis sie abgefragt (polled) wird. Die Grundidee: Anstatt einen Thread zu blockieren, registrieren wir eine Aufgabe und erhalten das Ergebnis, wenn es bereit ist.



use std::future::Future;use std::pin::Pin;use std::task::{Context, Poll};

struct MyFuture { ready: bool,}

impl Future for MyFuture { type Output = u32;

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<u32> { if self.ready { Poll::Ready(42) } else { // Waker für erneute Abfrage registrieren cx.waker().wake_by_ref(); Poll::Pending } }}


async/await – syntaktischer Zucker



Das Schlüsselwort async wandelt eine Funktion oder einen Block in einen Future um. await pausiert die Ausführung, bis der Future abgeschlossen ist, und gibt die Kontrolle an die Laufzeitumgebung zurück.



use tokio::time::{sleep, Duration};

async fn fetch_data() -> String { // Simulation einer Netzwerkanfrage sleep(Duration::from_secs(2)).await; String::from("Daten empfangen")}

#[tokio::main]async fn main() { let result = fetch_data().await; println!("{}", result);}


Lebenszyklen und der Borrow Checker



Eine Besonderheit von Rust ist die strenge Kontrolle von Ausleihen. In asynchronem Code ist dies besonders wichtig, da ein Future länger leben kann als die ausgeliehenen Daten. Verwenden Sie async move, um das Eigentum zu übernehmen:



async fn process_data(data: Vec<u32>) -> u32 {    data.iter().sum()}

#[tokio::main]async fn main() { let numbers = vec![1, 2, 3, 4, 5]; let sum = process_data(numbers).await; println!("Summe: {}", sum);}


Praktische Arbeit mit Tokio



Installation und Konfiguration



Tokio ist die beliebteste asynchrone Laufzeitumgebung für Rust. Fügen Sie in Cargo.toml hinzu:



[dependencies]tokio = { version = "1", features = ["full"] }


Erstellen eines asynchronen TCP-Servers



Betrachten wir einen einfachen Echo-Server, der Verbindungen annimmt und die empfangenen Daten zurücksendet:



use tokio::net::TcpListener;use tokio::io::{AsyncReadExt, AsyncWriteExt};

#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> { let listener = TcpListener::bind("127.0.0.1:8080").await?; println!("Server gestartet auf Port 8080");

loop { let (mut socket, addr) = listener.accept().await?; println!("Neue Verbindung von: {}", addr);

tokio::spawn(async move { let mut buf = [0; 1024]; loop { let n = socket.read(&mut buf).await.unwrap(); if n == 0 { break; } // Verbindung geschlossen socket.write_all(&buf[..n]).await.unwra

Blogs

Buchempfehlungen