Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Vecteurs et chaînes de caractères

En Rust, il y a deux types pour les chaînes de caractères (et pour les vecteurs de manière générale).

VecteursChaînes de caractères
Type pour la grosse structure où on peut changer la tailleVec<T>String
Type vue mémoire (taille, pointeur)slice<T>str

str

Le type str désigne un type dynamique dont la taille n’est pas connue. Donc on ne peut pas l’utiliser sur la pile. Donc, Rust offre &str (référence d’un str). Une référence, en gros, c’est un pointeur. Par exemple, &i32 est une adresse mémoire vers un entier i32. Mais pour str c’est mieux : un &str est une structure avec la longueur et un pointeur vers les caractères. Un &str peut se déclarer avec une chaîne de caractères brut, comme "Bonjour".

#![allow(unused)]
fn main() {
let s: &str = "Bonjour";
println!("{}", s);
}

On peut obtenir la longueur d’une chaîne avec .len() et c’est en O(1), contrairement au C :

#![allow(unused)]
fn main() {
let s = "Bonjour";
println!("{}", s.len());
}

Attention, on ne peut pas écrire *s car *s n’a pas de taille connue à la compilation (à la compilation on ne sait pas sur combien de caractères le pointeur pointe).

String : le type chaîne de caractères puissant

Un type String c’est comme un vecteur et on peut faire des choses avec.

#![allow(unused)]
fn main() {
let mut s = String::from("Bonjour");
s.push_str(" le monde !");
println!("{}", s); // Bonjour le monde !

s.push('?'); // Ajoute le caractère ?
println!("{}", s); // Bonjour le monde !?

s.pop(); // Supprime le dernier caractère
println!("{}", s); // Bonjour le monde !
}

Passage de l’un à l’autre

Un objet String c’est le propriétaire de la chaîne de caractères. On peut la modifier, la triturer, dans tous les sens. Mais attention, c’est un grosse usine à gaz. Ce n’est pas quelque chose de simple. Il y a un champ pour la capacité, la longueur, les données qui sont toujours sur le tas car cela doit être modifiable, etc.

Ainsi, &str est le texte de base, rapide, simple.

&str en String

Pour transformer une str en String, on utilise un constructeur String::from.

#![allow(unused)]
fn main() {
let x: &str = "Bonjour";
let s: String = String::from(x);
let y: &str = &s;
println!("{}", s);
}

String en &str

Pour avoir la &str totale d’un String : on déréférence.

#![allow(unused)]
fn main() {
let x: &str = "Bonjour";
let s: String = String::from(x);
let y: &str = &s;  // on déréférence
println!("{}", s);
}

On peut aussi avoir une sous-chaîne d’un &str ou d’un String en déférençant et en indiquant la tranche.

#![allow(unused)]
fn main() {
let s = "Bonjour";
let sous_chaine: &str = &s[3..5];
println!("{}", sous_chaine); // jo
}
#![allow(unused)]
fn main() {
let s = String::from("Bonjour");
let sous_chaine: &str = &s[3..5];
println!("{}", sous_chaine); // jo
}

Quiz

  • Que fait ce programme ?
#![allow(unused)]
fn main() {
let mut s = String::from("Bonjour");
s.pop();
let sous_chaine: &str = &s[3..5];
println!("{}", sous_chaine); // jo
}
  • Que fait ce programme ?
#![allow(unused)]
fn main() {
let mut s = String::from("Bonjour");
s.pop(); s.pop(); s.pop();
let sous_chaine: &str = &s[3..5];
println!("{}", sous_chaine); // jo
}

Accéder au i-ème caractère

C++ le libéral

C++ permet d’accéder au/modifier le i-ème caractère tranquille :

std::string s = "Bonjour";
s[3] = 'p';

Avantage : c’est en O(1). Inconvénient : c’est une modification d’un octet. Le problème est que le C++ ne garantit rien sur l’encodage, comme le montre ce morceau de programme :

#include <iostream>
#include <string>

int main() {
    std::string s = "Éléphant"; // 'É' prend 2 octets en UTF-8
    s[0] = 'A'; // modification du premier octet
    std::cout << s << std::endl; // Affiche un caractère bizarre
}

Rust le pénible

Rust n’offre pas de fonction pour modifier le i-ème caractère. Et pour cause ! Remplacer un a par une émoticône compliquée comme `` demande de tout décaler…

Du coup, Rust offre des fonctions pour accéder aux caractères. Contrairement à C/C++, le type char en Rust prend 4 octets pour pouvoir stocker n’importe quel caractère UTF-8.

Voici un programme qui modifie le 3e caractère :

#![allow(unused)]
fn main() {
let mut s = String::from("Bonjour");
let mut chars: Vec<char> = s.chars().collect();
chars[3] = 'x';
s = chars.into_iter().collect(); 
println!("{}", s);
}

La méthode .chars() produit un itérateur sur les caractères de la chaîne. Puis avec .collect() on rassemble les caractères dans une collection concrète, ici un vecteur de char. Une fois le troisième caractère modifié, on convertit notre vecteur en itérateur avec .into_iter(). Puis avec .collect(), on rassemble les caractères en une collection, mais cette fois une String (Rust le sait avec s = ... et s est une String).