Blog ENI : Toute la veille numérique !
🐠 -25€ dès 75€ 
+ 7 jours d'accès à la Bibliothèque Numérique ENI. Cliquez ici
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. Rust
  3. Possession et emprunt en Rust
Extrait - Rust Développez des programmes robustes et sécurisés
Extraits du livre
Rust Développez des programmes robustes et sécurisés
4 avis
Revenir à la page d'achat du livre

Possession et emprunt en Rust

Introduction

En Rust, on maîtrise totalement la gestion de la mémoire. Tant du point de vue de l’allocation que de celui de la désallocation. Cette opération s’effectue, bien sûr, sans garbage collector (ramasse-miettes) comme c’est le cas en C# ou en Java. On est significativement plus dans la situation du C++, dans laquelle la programmeuse ou le programmeur alloue et désalloue à sa guise.

Un ramasse-miettes est un dispositif en code informatique en charge de la désallocation automatique de la mémoire, dès lors qu’elle n’est plus utilisée (ou supposée l’être). La personne en charge du développement n’a plus alors (en théorie) à se poser la question de la libération de la mémoire allouée.

Il existe cependant une différence notable entre C++ et Rust : en C++, une situation problématique n’apparaîtra qu’à l’exécution, que ce soit le fait d’accéder à un espace mémoire déjà désallouée, ou encore que l’on essaie de stocker de la donnée dans un espace non encore alloué. En Rust, ce type de problème est détecté dès la compilation et entraîne un échec de celle-ci. En cas d’erreur, il convient de corriger le code, afin que la compilation...

Fonctionnement de la possession

1. Grands principes

Les principes de la possession sont extrêmement simples, c’est leur mise en œuvre concrète qui peut parfois s’avérer compliquée. Voici les deux principes en question :

1. Toute valeur en Rust a un propriétaire et un seul.

2. Quand le propriétaire sort de la portée, la valeur est supprimée.

Cela appelle trois remarques :

  • Une valeur peut changer de propriétaire. Auquel cas, le premier propriétaire n’a plus aucun pouvoir sur la valeur après cession.

  • Cela s’applique aussi bien à des valeurs stockées sur le tas qu’à celles stockées dans la pile.

  • L’unique propriété serait limitative : c’est pour cela que l’on peut emprunter une valeur (emprunt, borrowing). L’utiliser sans pouvoir la modifier, avant de la rendre.

2. Exemples relatifs à la propriété

Dans cet exemple, nous allons tâcher de détailler le fonctionnement de la possession, pour deux valeurs :

  • Une valeur entière stockée sur la pile.

  • Une valeur relative à une chaîne de caractères, stockée dans le tas.

L’exemple proposera différentes séquences dans lesquelles la compilation échoue car le code enfreint justement les grands principes de la possession.

a. Propriété dans le tas

On définit une chaîne de caractères allouée dans le tas, appartenant à la variable chaine. En termes de possession, la variable chaine possède la valeur "ENI" :

let chaine = String::from("ENI"); 

Puis il y a un déplacement à chaine2 :

let chaine2 = chaine; 

Si on essaie d’afficher chaine, on aura une erreur de compilation :

println!("{}", chaine); 

En effet, on essaie d’accéder à une valeur que le propriétaire a prêtée à une autre variable (chaine2). On obtient donc ce message d’erreur :

error[E0382]: borrow of moved value: `chaine` 
--> src/main.rs:9:20 
  | 
6 |     let chaine = String::from("ENI"); 
  |         ------ move occurs because `chaine` has type `String`, 
which does...

Fonctionnement de l’emprunt

1. Introduction

Pour éviter d’être trop souvent contraint de céder une propriété de valeur, il est possible en Rust de faire une sorte de prêt d’une valeur. On appelle cela emprunt. Pour ce faire, la notion de référence abordée plusieurs fois depuis le début de ce livre est utilisée.

Une référence, pour rappel, est une sorte de pointeur, c’est-à-dire que c’est une adresse mémoire. En plus, une référence pointe vers quelque chose de tangible, en vigueur. Par ailleurs, une référence est relative à un type donné. Exprimé autrement, une référence représente une adresse à laquelle une valeur d’un type donné est stockée.

Nous avons déjà vu un usage particulier des références en Rust, avec les slices (tranches). Le principe de son usage ici est globalement le même que ce que nous avons évoqué dans le précédent chapitre.

Voyons d’abord un exemple « classique » : on a une chaîne de caractères et on souhaite coder une fonction à même de calculer la longueur de cette chaîne de caractères.

  • D’abord, en n’utilisant que la propriété (il faudra donc transmettre la propriété de la chaîne en question à la fonction de calcul).

  • Ensuite, on procède de même, mais en utilisant l’emprunt, ce qui devrait participer à démontrer la praticité de ce mécanisme.

2. Exemple : transfert de propriété vs emprunt

a. Transfert de propriété

fn main() { 
   let...

Conclusion

La propriété (ainsi que l’emprunt) est fondamentale en Rust. C’est cette notion qui permet de sécuriser la mémoire en Rust et ainsi d’éviter des erreurs d’accès à de la mémoire déjà libérée, ou non correctement allouée, comme on peut le subir en C++ par exemple.

Si la notion n’est pas toujours simple à appréhender, elle est tout de même facile à respecter pour le développeur Rust car tout se joue à la compilation, avec des messages d’erreur particulièrement explicites.

Autrement dit, dès que les transferts de propriétés, ou les prêts, de votre code sont cohérents selon le compilateur Rust, la compilation se termine par un succès et vous obtenez ainsi une sorte de garantie que l’exécution ne subira pas d’incident à propos de la gestion de la mémoire.