Aller au contenu

Les Vecteurs en Rust : des listes modifiables

Dans cet article, on va plonger dans un type de collection essentiel en Rust : les vecteurs. Si tu as déjà travaillé avec des tableaux dynamiques dans d’autres langages (comme les listes en Python ou les tableaux redimensionnables en C++), alors les vecteurs te seront familiers. En Rust, les vecteurs sont extrêmement utiles pour stocker et manipuler des ensembles de données de taille dynamique, tout en garantissant la sécurité mémoire.

Qu’est-ce qu’un Vecteur ?

Un vecteur en Rust est une collection qui peut stocker plusieurs valeurs dans un ordre séquentiel. Contrairement aux tableaux (array), dont la taille est fixe, un vecteur peut croître ou diminuer dynamiquement en fonction des besoins. Les éléments d’un vecteur doivent être du même type.

En Rust, les vecteurs sont représentés par la structure Vec, où T est le type des éléments du vecteur.

Voici un exemple basique de création d’un vecteur :

fn main() {
let mut v: Vec<i32> = Vec::new(); // Création d'un vecteur vide
v.push(10); // Ajout d'un élément
v.push(20);
println!("{:?}", v); // Affiche [10, 20]
}

Dans cet exemple, on crée un vecteur vide avec Vec::new() et on ajoute des éléments avec la méthode push().

Différentes façons de créer un vecteur

Rust offre plusieurs moyens de créer des vecteurs. Voici quelques exemples :

1. Utiliser Vec::new()

C’est la méthode la plus basique pour créer un vecteur vide :

let mut v: Vec<i32> = Vec::new();

2. Utiliser la macro vec![]

L’approche la plus simple et la plus courante est d’utiliser la macro vec![], qui permet de créer un vecteur avec des éléments initiaux :

let v = vec![1, 2, 3, 4, 5];

3. Avec une capacité prédéfinie

Si tu connais à l’avance la taille que prendra ton vecteur, tu peux l’initialiser avec une capacité définie. Cela évite des réallocations coûteuses si le vecteur grandit souvent :

let mut v = Vec::with_capacity(10);

Manipuler les éléments d’un vecteur

Les vecteurs en Rust permettent une grande flexibilité pour ajouter, accéder et modifier les éléments.

1. Ajouter des éléments

Comme vu précédemment, la méthode push() permet d’ajouter un élément à la fin d’un vecteur :

let mut v = vec![1, 2, 3];
v.push(4); // Ajoute 4 à la fin

2. Accéder aux éléments

Pour accéder aux éléments d’un vecteur, tu peux utiliser l’indexation, mais il y a deux façons de le faire en Rust :

Utiliser l'index directement, mais cela panique si l'index est hors des limites :
let v = vec![10, 20, 30];
let premier = v[0]; // Accès via index
println!("{}", premier); // Affiche 10

Utiliser la méthode get(), qui retourne une option (Option<&T>) afin de prévenir les accès hors limites :

let v = vec![10, 20, 30];
if let Some(valeur) = v.get(2) {
println!("Le troisième élément est {}", valeur);
}

3. Modifier des éléments

Pour modifier un élément, il faut une référence mutable :

let mut v = vec![1, 2, 3];
v[1] = 10; // Remplace le deuxième élément
println!("{:?}", v); // Affiche [1, 10, 3]

4. Supprimer des éléments

Pour supprimer des éléments, tu peux utiliser pop() pour retirer le dernier élément du vecteur :

let mut v = vec![1, 2, 3];
v.pop(); // Supprime 3

pop() retourne un Option, qui contient la valeur supprimée si elle existe, ou None si le vecteur est vide.

Propriété et emprunts dans les vecteurs

Rust impose des règles strictes sur la gestion des références, même pour les vecteurs. Lorsque tu empruntes une référence mutable à un vecteur (&mut), tu ne peux pas emprunter une référence partagée (&) au même vecteur en même temps.

Prenons un exemple simple :

let mut v = vec![1, 2, 3];
// Créer une référence mutable
let premier = &mut v[0];
// Ce qui suit causerait une erreur, car on ne peut pas avoir
// une référence partagée et mutable en même temps
// let second = &v[1]; // Erreur de compilation

Pour éviter ces erreurs, assure-toi que les emprunts mutables et partagés ne se chevauchent pas.

Parcourir un vecteur

Tu peux parcourir un vecteur de plusieurs façons en Rust :

1. Avec une boucle for

La façon la plus simple est d’utiliser une boucle for pour parcourir chaque élément :

let v = vec![1, 2, 3, 4, 5];
for elem in &v {
println!("{}", elem);
}

2. Itérateurs

Rust propose aussi un système d’itérateurs très puissant. Tu peux obtenir un itérateur avec la méthode iter() :

let v = vec![1, 2, 3];
for elem in v.iter() {
println!("{}", elem);
}

Pour modifier les éléments d’un vecteur pendant l’itération, utilise iter_mut() :

let mut v = vec![1, 2, 3];
for elem in v.iter_mut() {
*elem += 1;
}
println!("{:?}", v); // Affiche [2, 3, 4]

Les vecteurs et la gestion mémoire

Rust gère la mémoire des vecteurs de manière très efficace grâce à son système de pile et tas. Quand tu crées un vecteur, les métadonnées (comme la taille, la capacité et un pointeur vers les éléments) sont stockées sur la pile, mais les éléments eux-mêmes sont stockés sur le tas.

Chaque fois que tu ajoutes des éléments au vecteur, Rust peut réallouer de l’espace sur le tas si la capacité est dépassée. C’est pour cela qu’il est souvent utile de pré-allouer de la capacité si tu sais à l’avance combien d’éléments tu vas stocker. Performances des vecteurs

En termes de performances, les vecteurs offrent un accès en temps constant (O(1)) à un élément via son index. Cependant, certaines opérations, comme l’insertion ou la suppression d’éléments, peuvent être plus coûteuses si elles nécessitent de déplacer plusieurs éléments.

Si tu ajoutes souvent des éléments à la fin du vecteur, l’opération reste efficace. En revanche, insérer ou supprimer des éléments au milieu du vecteur peut être plus coûteux, car cela entraîne des décalages d’éléments.

Conclusion

Les vecteurs sont une structure de données flexible et puissante en Rust, permettant de stocker des données dynamiques avec un contrôle total sur la gestion mémoire. En comprenant comment les utiliser efficacement et en respectant les règles de propriété et d’emprunt, tu pourras tirer le meilleur parti des vecteurs dans tes projets.

Que ce soit pour des collections simples ou pour des structures de données plus complexes, les vecteurs sont un outil essentiel à maîtriser pour écrire des programmes performants et sûrs en Rust. N’hésite pas à expérimenter avec les vecteurs et à découvrir leur flexibilité !