Propriété, portée et déplacements

RustBeginner
Pratiquer maintenant

Introduction

Rust utilise la propriété pour déterminer quelle partie d’un programme est responsable d’une valeur et à quel moment cette valeur peut être libérée. Considérez la propriété comme une responsabilité et une autorisation, et non comme une étiquette permanente attachée à un seul nom de variable : cette responsabilité peut être déplacée vers une nouvelle liaison. Cela évite plusieurs erreurs de mémoire sans nécessiter de ramasse-miettes.

Dans cet atelier, vous constaterez d’abord qu’un petit entier peut être copié. Vous demanderez ensuite volontairement à Rust d’utiliser une String possédée après son déplacement, vous lirez le diagnostic du compilateur, puis vous corrigerez le flux de données en utilisant la propriété renvoyée par une fonction. Le squelette de code est déjà préparé afin que chaque modification reste courte et ciblée.

Observer la portée et un entier copié

Dans cette étape, vous allez copier un petit entier dans une portée interne et vérifier que la liaison d’origine reste utilisable.

Accédez au projet préparé et ouvrez le fichier source :

cd /home/labex/project/ownership-demo
nano src/main.rs

Une portée est la région dans laquelle une liaison est valide. Les accolades créent une nouvelle portée interne. La liaison local_count n’existe qu’entre la paire d’accolades interne, tandis que warehouse_count appartient à la portée main environnante.

Sur la ligne de l’étape 1, remplacez uniquement 0 par warehouse_count :

        let local_count = warehouse_count; // Step 1: replace the placeholder.

Le type entier i32 implémente le comportement Copy de Rust. Son affectation à local_count copie la petite valeur au lieu d’invalider warehouse_count.

Enregistrez avec Ctrl+O, appuyez sur Entrée, puis quittez avec Ctrl+X. Exécutez le programme :

cargo run --quiet

Les deux premières lignes prouvent que les deux liaisons peuvent être utilisées :

Local count: 3
Warehouse count: 3

Lorsque la portée interne se termine, local_count cesse d’être valide. La liaison externe warehouse_count reste valide jusqu’à la fin de main.

Lire une erreur de déplacement intentionnelle

Dans cette étape, vous allez utiliser volontairement une String après son déplacement, puis interpréter le diagnostic du compilateur Rust. L’échec de la vérification est attendu et constitue le résultat pédagogique de cette étape.

String::from("PX-104") crée du texte possédé. Contrairement à un petit i32, une String gère un tampon de texte alloué séparément, dont la taille peut changer. Rust attribue la responsabilité de ce tampon à un seul propriétaire afin de pouvoir le libérer exactement une fois.

L’appel record_parcel(parcel_id) transmet la String par valeur. La propriété est déplacée de la liaison parcel_id vers la fonction. La fonction renvoie la valeur, et l’appel stocke ce propriétaire renvoyé dans _returned_id. Le caractère de soulignement initial indique à Rust que cette liaison temporaire n’est volontairement pas utilisée pour le moment.

La valeur n’est pas détruite lorsqu’elle est déplacée. Seule l’ancienne liaison perd l’autorisation de l’utiliser. Le parcours de la propriété dans ce programme est le suivant :

parcel_id in main → parcel_id parameter in record_parcel → _returned_id in main

À chaque étape, un propriétaire courant reste responsable du même texte.

Ouvrez le fichier :

nano src/main.rs

Repérez la ligne située sous le commentaire de l’étape 2 et supprimez uniquement les // initiaux afin d’obtenir :

    println!("Original after move: {parcel_id}");

Enregistrez et quittez. Avant d’exécuter la commande suivante, prévoyez le résultat : parcel_id n’est plus le propriétaire, le compilateur devrait donc refuser cette utilisation.

Lancez la vérification. Elle doit échouer :

cargo check

Repérez les parties stables suivantes du diagnostic :

error[E0382]: borrow of moved value: `parcel_id`
...
value moved here
...
value borrowed here after move

Le mot borrow apparaît parce que println! a seulement besoin d’un accès temporaire pour afficher la valeur. Cet accès reste impossible via parcel_id, car la propriété a déjà été déplacée. À l’étape suivante, vous utiliserez la liaison qui a récupéré la propriété depuis la fonction.

Continuer avec le propriétaire renvoyé

Dans cette étape, vous allez corriger l’erreur de déplacement en nommant et en utilisant la valeur renvoyée par record_parcel.

La signature de fonction fn record_parcel(parcel_id: String) -> String indique que la fonction prend la propriété d’une String et renvoie une String. Son expression finale parcel_id transfère la propriété à l’appelant.

Ouvrez le fichier :

nano src/main.rs

Effectuez deux petites modifications. Commencez par renommer _returned_id en returned_id sur la ligne d’appel de la fonction. Cela supprime le préfixe de soulignement temporaire, puisque vous êtes sur le point d’utiliser la valeur.

Remplacez ensuite la ligne d’affichage en échec par la ligne suivante :

    println!("Returned owner: {returned_id}");

L’ancienne liaison parcel_id reste invalide après le déplacement. Renommer _returned_id en returned_id ne rétablit pas l’ancienne liaison ; cela donne un nom normal à la liaison qui a déjà récupéré la propriété. returned_id est maintenant le propriétaire courant et constitue donc le nom correct à utiliser.

Enregistrez et quittez, puis vérifiez le programme corrigé :

cargo check

Un résultat Finished sans l’erreur E0382 confirme que le flux de propriété est valide. Exécutez le programme :

cargo run --quiet

La sortie complète doit être :

Local count: 3
Warehouse count: 3
Recorded parcel: PX-104
Returned owner: PX-104

À la fin de main, returned_id quitte sa portée et Rust libère sa String possédée. Il n’y a toujours qu’un seul propriétaire responsable de ces données.

Résumé

Vous avez mis en relation la portée lexicale et la durée de vie des valeurs, observé la copie d’un i32 sans invalider sa liaison d’origine, suivi le déplacement d’une String vers une fonction, interprété E0382 et continué en toute sécurité avec la propriété renvoyée à l’appelant.