Les chaînes de caractères en Rust
Introduction
Nous avons déjà entraperçu et manipulé à plusieurs reprises des chaînes de caractères en langage Rust, notamment avec String et str. L’implantation en mémoire a également été abordée, mais nous y reviendrons ici.
Nous en profiterons pour expliquer le rôle de char et son utilisation en Rust. Nous verrons surtout les différentes manières d’utiliser et de manipuler des caractères en langage Rust.
Commençons par évoquer les questions d’encodage, Unicode et UTF-8, qui sont la base des caractères en Rust (char) et des chaînes de caractères (String, str).
Encodages Unicode et UTF-8 et caractères en Rust
1. Quelques définitions
Unicode est un standard logiciel qui permet d’encoder n’importe quel caractère issu de toutes les langues du monde.
UTF-8 (Universal Character Set Transformation Format - 8 bits) est un codage de caractères conçu pour encoder l’ensemble des caractères du répertoire universel. UTF-8 est totalement compatible avec le standard Unicode.
2. Encodage en Rust
a. Le type char et l’Unicode
Le caractère seul (char) correspond en langage Rust à une valeur numérique Unicode (ce que l’on nomme dans le jargon un « point de code »).
Concrètement, un char en Rust se code sur 32 bits (4 octets).
b. Les types String et str et l’UTF-8
String et str partagent le fait de correspondre à un encodage UTF-8.
Le type str est véritablement le type primitif de chaînes de caractères en Rust. On dit que ce type est toujours valide au sens de UTF-8.
On a déjà rencontré ce type dans ce livre, en particulier sous les deux écritures suivantes correspondant à un emprunt :
&str
&'static str
Le type String correspond à une chaîne extensible encodée en UTF-8. Pourquoi extensible ? Car elle n’est ni plus ni moins qu’un vecteur de caractères encodés sur un entier...
À la découverte des caractères (char) en Rust
1. Nature des caractères
Commençons par créer un projet support pour la présente section et pour la suivante consacrée à String et str :
cargo new projet_car_chaine --bin
Created binary (application) `projet_car_chaine` package
Rust fournit plusieurs méthodes visant à connaître la nature d’un caractère. Ces méthodes permettent ainsi de préciser si le caractère est numérique (is_numeric), alphabétique (is_alphabetic), alphanumérique (is_alphanumeric), ou encore s’il est un caractère espace (is_whitespace) ou un caractère dit « de contrôle » (is_control).
On crée donc des variables stockant des caractères, c’est-à-dire quelque chose stocké sur 32 bits, par défaut sur la pile :
fn main() {
// Des caractères.
let quatre = '4';
println!("'4' est numérique ? : {}", quatre.is_numeric());
let a = 'a';
println!("'a' est alphabétique ? : {}", a.is_alphabetic());
let etoile...
À la découverte de String et str
1. Introduction
Rappelons que ces deux types chaînes de caractères n’incluent que des valeurs UTF-8 bien formées, c’est-à-dire que le compilateur Rust garantit cela. Toutes les opérations conduites sur des types chaînes de caractères (String ou str) sont garanties et maintiennent un contenu bien formé au sens de UTF-8.
2. Le type str
Le type str ou plutôt &str correspond à une séquence d’octets en UTF-8 qui est non mutable. Par ailleurs, la taille de la chaîne de caractères n’est pas connue (contrairement à String). C’est ce que l’on appelle une tranche (slice) de chaîne de caractères.
La documentation en ligne de ce type est disponible à cette adresse : https://doc.rust-lang.org/std/str/index.html
On peut définir ce type de façon implicite, mais on peut également le préciser de manière explicite en indiquant &’static str.
fn fonction_str() {
let valeur_str = "bonjour";
println!("valeur_str : {}", valeur_str);
let valeur_str_2 : &'static str = "BONJOUR";
println!("valeur_str_2 : {}", valeur_str_2);
}
En sortie, on obtient ceci :
valeur_str : bonjour
valeur_str_2 : BONJOUR
Le type &str n’est pas de taille fixe, ni en réalité de taille connue. Il ne peut donc implémenter le trait Clone. Si l’on veut copier une valeur de ce type, il faut recourir à un autre trait : ToOwned. Ainsi, on peut utiliser la méthode to_owned, qui renvoie une copie de la chaîne de caractères :
let valeur_str_3 = valeur_str_2.to_owned();
println!("valeur_str_3 : {}", valeur_str_3);
On peut également étudier une chaîne de caractères de type &str en ayant recours aux tranches. On accède à la valeur de diverses tranches. On peut vérifier également la longueur d’une tranche et inspecter son contenu :
println!("&valeur_str_3[..3] : {}", &valeur_str_3[..3]);
println!("&valeur_str_3[3..] : {}", &valeur_str_3[3..]); ...
Un mot sur les expressions régulières
On désigne par « expression régulière » un outil qui permet, dans la plupart des langages de programmation évolués, de requêter des chaînes de caractères selon un motif ou une séquence définie. Il existe une similarité avec le filtrage par motif.
Néanmoins, Rust fournit aussi une caisse dédiée aux expressions régulières car elles s’avèrent plus efficaces et performantes dans certains cas précis.
La caisse qui contient le nécessaire s’appelle regex. On doit donc référencer ainsi cette caisse pour l’utiliser :
extern crate regex
La documentation de cette caisse est disponible à cette adresse : https://docs.rs/regex/latest/regex/
La documentation sur crates.io est quant à elle accessible ici : https://crates.io/crates/regex
Prenons un petit exemple. On reçoit des dates au format suivant : YYYY-MM-JJ. On souhaite extraire respectivement l’année, le mois et le jour de ce format. Nous allons utiliser les expressions régulières en Rust.
On commence par ajouter une dépendance dans le fichier cargo.toml :
[dependencies]
regex = "1.5"
Puis on peut commencer à établir notre code. On référence la caisse et le module dont on aura...