Déplacer la sémantique dans Rust

Nilesh Katuwal 8 octobre 2023
Déplacer la sémantique dans Rust

Dans cet article, nous allons découvrir la sémantique de déplacement dans Rust.

Déplacer la sémantique dans Rust

Dans Rust, tous les types sont modifiables et toutes les opérations de déplacement équivalent à une copie partielle des données d’origine au nouvel emplacement. Move convertit toutes les variables capturées par référence ou référence mutable en variables capturées par valeur.

Lorsque des threads sont impliqués, le mouvement est couramment utilisé.

Les processus d’élimination de la rouille sont également destructeurs. Par exemple, après avoir quitté une variable, la variable n’est plus utilisable dans le code.

Dans Rust, un mouvement est systématiquement mis en œuvre en copiant les données dans l’objet lui-même et en ne détruisant pas l’objet d’origine. Il n’est jamais implémenté en copiant les ressources gérées de l’objet ou en exécutant du code personnalisé.

Rust fait une exception pour les types qui ne nécessitent pas de sémantique de déplacement. Au lieu de cela, la valeur contient toutes les informations nécessaires pour la représenter, et aucune allocation de tas ou ressource n’est gérée par la valeur, telle que i32.

Ces types implémentent le trait spécial Copier car la copie est peu coûteuse et constitue la méthode par défaut pour passer aux fonctions ou gérer les affectations :

fn foo(bar: i32) {
    // Implementation
}

let var: i32 = 9;
foo(var); // copy
foo(var); // copy
foo(var); // copy

L’appel de fonction par défaut utilise la sémantique de déplacement pour les types autres que Copy, comme String. Dans Rust, lorsqu’une variable est déplacée, sa durée de vie se termine prématurément.

Au moment de la compilation, le déplacement remplace l’appel du destructeur à la fin du bloc, c’est donc une erreur d’écrire le code suivant pour String :

fn foo(bar: String) {
    // Implementation
}

let var: String = "Hello".to_string();
foo(var); // Move
foo(var); // Compile-Time Error
foo(var); // Compile-Time Error

Clone rust nécessite des implémentations, mais c’est identique pour tous les déplacements : copiez la mémoire dans la valeur et n’appelez pas le destructeur de la valeur d’origine.

Et dans Rust, avec cette implémentation exacte, tous les types sont mobiles ; les types non mobiles n’existent pas (bien que les valeurs non mobiles existent). Les octets doivent encoder des informations sur la ressource que la valeur gère, comme un pointeur, dans le nouvel emplacement aussi efficacement qu’ils le faisaient dans l’emplacement précédent.

Rust a un mécanisme connu sous le nom d’épinglage qui indique dans le système de type qu’une valeur spécifique ne bougera plus jamais, qui peut être utilisée pour implémenter des valeurs auto-référentielles et est utilisée en asynchrone.

Exemple:

fn destroy_box(z: Box<i32>) {
    println!("The box that contains {} is destroyed", z);
}

fn main() {
    let a = 5u32;
    let b = a;
    println!("a is {}, and y is {}", a, b);

    let m = Box::new(5i32);

    println!("a contains: {}", m);
    let n = m;
    destroy_box(n);
}

La fonction destroy_box prend possession de la mémoire allouée par tas dans le code source ci-dessus. D’abord, le z est détruit, et la mémoire est libérée.

m est un pointeur vers un entier alloué par tas.

Production:

a is 5, and y is 5
a contains: 5
The box that contains 5 is destroyed