Iteration über einen String in Rust: Wie man Zeichen ohne Schmerz durchläuft

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

Iterieren über einen String in Rust: Wie es unter der Haube funktioniert



Hast du dich jemals gefragt, was eigentlich passiert, wenn du for c in "Привет".chars() schreibst? Wenn du von Python oder JavaScript kommst, wo Strings einfach Arrays von Zeichen sind, kann Rust wie ein strenger Lehrer wirken, der dich dazu bringt, vor jeder Aktion nachzudenken. Und das ist großartig!



Die Frage "Wie iteriert man über einen String zeichenweise" auf Stack Overflow hat tausende Aufrufe gesammelt. Und das aus gutem Grund. In Rust gibt es kein einfaches string[i] — und dafür gibt es gute Gründe. Lass uns herausfinden, wie man über einen String korrekt, effizient und ohne Überraschungen iteriert.



Warum erlaubt Rust es nicht einfach, ein Zeichen per Index zu erhalten?



Es dreht sich alles um die UTF-8-Kodierung. Im Gegensatz zu vielen Sprachen speichert Rust Strings in UTF-8, nicht in ASCII oder UCS-2. Das bedeutet, dass ein Zeichen 1 bis 4 Bytes belegen kann. Zum Beispiel:



  • 'a' — 1 Byte
  • 'é' — 2 Bytes
  • '中' — 3 Bytes
  • '😊' — 4 Bytes


Wenn Rust string[0] erlauben würde, dann würde "😊"[0] nicht das Emoji zurückgeben, sondern die Hälfte seiner Byte-Darstellung — Müll. Daher schützt Rust dich auf Compiler-Ebene vor solchen Fehlern.



Hauptwege, um über einen String zu iterieren



Rust bietet drei Haupt-iterationsmethoden, jede mit ihrem eigenen Zweck. Die Wahl hängt davon ab, was genau du benötigst: Zeichen, Bytes oder Grapheme.



1. Iteration über Zeichen (char)



Die gebräuchlichste Methode ist die .chars()-Methode. Sie gibt einen Iterator über Unicode-Zeichen (Typ char) zurück.



fn main() {    let text = "Привет, мир! 🌍";        for c in text.chars() {        println!("{}", c);    }        // Ausgabe:    // П    // р    // и    // в    // е    // т    // ,    // (Leerzeichen)    // м    // и    // р    // !    // (Leerzeichen)    // 🌍}


Unter der Haube parst .chars() die UTF-8-Bytereihenfolge und gibt jedes gültige Zeichen zurück. Wenn der String ungültige Bytes enthält, kann der Iterator std::char::REPLACEMENT_CHARACTER ('�') zurückgeben.



Wichtig: .chars() ist O(n). Der Iterator geht durch alle Bytes und dekodiert sie im laufenden Betrieb. Für kurze Strings ist das sofort erledigt, aber für lange (Megabytes an Text) kann es spürbar sein.



2. Iteration über Bytes (bytes)



Wenn du rohe Bytes benötigst, verwende .bytes(). Dies ist ein schneller Weg, aber du erhältst Bytes, keine Zeichen.



fn main() {    let text = "Hello";        for byte in text.bytes() {        println!("{}", byte);    }        // Ausgabe:    // 72 (H)    // 101 (e)    // 108 (l)    // 108 (l)    // 111 (o)}


Für englischen Text ist dies äquivalent zum Iterieren über Zeichen. Aber für Russisch oder Emojis ist es das nicht. Zum Beispiel ist "Привет" in Bytes 12 Bytes (2 pro Zeichen) und "🌍" — 4 Bytes.



Wann verwenden: wenn du mit binären Daten, Protokollen arbeitest oder maximale Leistung ohne UTF-8-Dekodierung wünschst.



3. Iteration über Graphemcluster



Das ist jetzt interessanter. Manchmal besteht ein visuelles "Zeichen" aus mehreren Unicode-Zeichen. Zum Beispiel eine Landesflagge oder ein Buchstabe mit diakritischen Zeichen: "é" kann als einzelnes Zeichen é (U+00E9) oder als Kombination von e + ◌́ (U+0065 + U+0301) dargestellt werden.



Um mit Graphemen zu arbeiten, musst du die Crate unicode-segmentation hinzufügen:



// Cargo.toml// [dependencies]// unicode-segmentation = "1.10"

use unicode_segmentation::UnicodeSegmentation;

fn main() { let text = "é"; // chars() gibt 1 Ze

Blogs

Buchempfehlungen