Indexation des chaînes en Rust : 5 erreurs fatales et comment les éviter

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Pourquoi Rust ne vous permet-il pas simplement d'écrire s[0] ?

\n\n

Vous commencez tout juste à apprendre Rust, vous écrivez votre premier "Hello, world!" et soudain... une erreur de compilation. Vous essayez d'obtenir le premier caractère de la chaîne, comme vous l'avez fait des centaines de fois en Python ou JavaScript, et le compilateur renvoie quelque chose comme the type 'String' cannot be indexed by '{integer}'. Cela vous dit quelque chose ?

\n\n

Cette question est l'une des plus populaires sur Stack Overflow. Des milliers de développeurs butent sur cette apparente "étrangeté" de Rust. Mais en réalité, derrière cela se cache une solution géniale et réfléchie qui protège votre code de bugs difficiles à détecter. Dans cet article, nous allons analyser les erreurs courantes lors de l'indexation des chaînes en Rust et, surtout, apprendre à le faire correctement.

\n\n

Erreur n°1 : Tentative d'indexation directe comme en C ou Python

\n\n

La première et la plus courante des erreurs est d'essayer d'écrire let c = my_string[0];. En Rust, les chaînes ne sont pas simplement des tableaux de caractères ASCII. Elles sont stockées en encodage UTF-8. Et cela change tout.

\n\n

Pourquoi cela ne fonctionne-t-il pas ?

\n

Imaginez que vous ayez la chaîne "Привет". Elle contient 6 "caractères" (du point de vue humain). Mais en mémoire, elle occupe 12 octets (chaque lettre cyrillique fait 2 octets). Si Rust autorisait my_string[0], que vous renverrait-il ? Le premier octet (qui ne veut rien dire en soi) ou le premier caractère ? Et si la chaîne contenait un emoji, qui occupe 4 octets ? Ce serait le chaos total.

\n\n

Rust garantit la sécurité au niveau de la compilation. L'indexation par un nombre entier fonctionne toujours en O(1) et renvoie un seul élément. Mais pour UTF-8, c'est impossible : un seul "caractère" (char) peut occuper de 1 à 4 octets. C'est pourquoi le compilateur interdit tout simplement cette opération, pour éviter que vous n'obteniez accidentellement des "déchets" ou que vous ne cassiez l'encodage.

\n\n
// ❌ Erreur de compilation !\nfn main() {\n    let s = String::from("Привет");\n    // let first = s[0]; // error[E0277]: the type 'String' cannot be indexed by '{integer}'\n    println!("Привет, мир!");\n}
\n\n

Erreur n°2 : Utilisation naïve des slices (tranches) sans tenir compte des limites des caractères

\n\n

Oui, les slices fonctionnent en Rust. Vous pouvez écrire &s[0..5]. Mais si vous tombez "à l'intérieur" d'un caractère multi-octets, votre programme plantera avec une panique lors de l'exécution. C'est ce qu'on appelle une runtime panic.

\n\n

Exemple d'erreur fatale

\n
fn main() {\n    let s = String::from("Здравствуйте"); // 12 caractères, mais 24 octets\n    // Essayons de prendre les 3 premiers octets\n    let slice = &s[0..3]; // 'З' occupe 2 octets, 'д' — 2 octets.\n    // 3 octets, c'est la moitié du deuxième caractère !\n    println!("{}", slice); // thread 'main' panicked at 'byte index 3 is not a char boundary'\n}
\n\n

Ce n'est pas un simple avertissement. C'est un crash complet du programme. Rust ne pardonne pas le travail non sécurisé avec la mémoire, même à l'exécution. Si vous travaillez avec des entrées utilisateur (pseudos, commentaires), vous obtiendrez inévitablement une telle panique dès le premier caractère cyrillique ou asiatique.

\n\n

Comment l'éviter : Utilisez la méthode char_indices() pour trouver les limites en toute sécurité ou travaillez via chars().

\n\n

Erreur n°3 : Confusion entre octets, char et graphèmes

\n\n

C'est l'erreur la plus sournoise. Même si vous apprenez à obtenir un char à partir d'une chaîne, vous pouvez encore obtenir autre chose que ce que vous attendiez. Parce que char en Rust est une valeur scalaire Unicode. Et ce qu'une personne considère comme un "caractère" (par exemple, "é" ou "🇷🇺") peut être composé de plusieurs char.

\n\n

Analyse avec un exemple

\n
fn main() {\n    let s = String::from("é"); // Cela peut être un seul char (U+00E9)\n    // ou un composé : 'e' (U+0065) + '◌́' (U+0301)\n    let composed = String::from("e\\u{0301}"); // le même 'é', mais composé de deux char\n    println!("Longueur de s : {} octets, {} char", s.len(), s.chars().count());\n    println!("Longueur de comp                

Blogs

Book Recommendations