web-dev-qa-db-fra.com

Pourquoi les matrices DO & STR sont-elles dans Rust passée sous forme de paramètres ont une durée de vie différente?

Je suis dans le processus d'apprentissage Rust et testez-vous un tableau de copie à travers une fonction. Je suis sûr qu'il y a une intégrée Rust fonctions à copier/clone Informations de tableau, mais une mise en œuvre personnelle que j'ai pensé serait une bonne idée d'aider ma compréhension de transmettre des références à travers des fonctions.

fn copy_str_arr_original (a1: [&str; 60], a2: &mut [&str; 60]) {
    // copy 1 into 2
    for i in 0..60 {
        a2[i] = a1[i];
    } // change is reflected in a2 as it is passed as &mut
}

Cependant, cela a jeté l'erreur these two types are declared with different lifetimes... pour le &str Types eux-mêmes. Après quelques études supplémentaires, j'ai essayé de déclarer ma propre vie et de les assigner, et cela le corrigé!

fn copy_str_arr_fix<'a> (a1: [&'a str; 60], a2: &mut [&'a str; 60]) {
    // copy 1 into 2
    for i in 0..60 {
        a2[i] = a1[i];
    } // change is reflected in a2 as it is passed as &mut
}

Pourquoi est-ce le cas, cependant? Pourquoi le type de valeurs dans la matrice doit avoir une vie attribuée au lieu des paramètres eux-mêmes? En d'autres termes, pourquoi cela ne fonctionne-t-il pas du tout?

fn copy_str_arr_bad<'a> (a1: &'a [&str; 60], a2: &'a mut [&str; 60]) {
    // does not work...           ^-----------------------^-------- different lifetimes
    for i in 0..60 {
        a2[i] = a1[i]; 
    } 
}

Je me débats toujours pour que la durée de vie travaille dans le contexte d'objets plus complexes tels que des tableaux et des structures, de sorte que toute explication serait grandement appréciée!

6
jts

La réponse simple est que le compilateur n'est pas très intelligent.

Le fait que vous n'ayez pas à spécifier de la durée de vie à chaque fois que vous définissez une fonction qui gère des références est seulement parce que le compilateur prend quelques suppositions éduquées si elle peut . C'est donc un peu intelligent, mais pas très.

Dites que vous écrivez une fonction qui prend une référence à une structure et retourne une référence à un champ de cette structure:

struct Book {
  pages: u16,
  title: String,
}

fn borrow_title(book: &Book) -> &str {
  &book.title
}

Neuf fois sur dix, c'est en effet une référence à l'argument que vous avez passé. Mais parfois ce n'est pas:

fn borrow_title(book: &Book) -> &'static str {
  if book.pages > 10 {
    "Too long..."
  } else {
    "Not long enough"
  }
}

Comme vous pouvez le constater, vous devrez spécifier que le retour renvoyé &str A une durée de vie différente (dans ce cas le spécial 'static.

Donc, depuis que vous dites fn copy_str_arr_original (a1: [&str; 60], a2: &mut [&str; 60]), le compilateur ne raisonne pas réellement de votre implémentation et ne sait pas que la durée de vie des références dans a1 Devrait être au moins Tant que la durée de vie de toute référence en a2.

Quant à la deuxième partie, vous devez considérer qu'une référence est simplement un pointeur à certaines données. Ces données peuvent contenir d'autres références. Dans ce cas, ce sont ces autres références qui sont importantes.

Vous avez 2 tableaux de références de chaîne ici. Dites que vous copiez les références du premier au deuxième. Que vous transmettez ces tableaux dans la fonction par référence ou non n'est pas important. Ce qui est important, c'est que si tout ce qui détient la propriété du premier tableau a été abandonné, les cordes seraient aussi. Et si la deuxième matrice a toujours détenu des références, cela entraînerait une manipulation de la mémoire dangereuse.

Pour simplifier, considérons qu'il n'y a qu'une seule chaîne et nous allons emprunter des valeurs dans un tableau, puis copiez ces valeurs empruntées à un autre tableau, déposez le premier tableau, puis laissez tomber la chaîne. Que vous attendriez-vous?

Le compilateur jettera un ajustement afin de s'assurer qu'aucune référence à la chaîne ne reste restée.

0
Kendas