Parfois, je me suis retrouvé à vouloir écrire des fonctions pouvant être appelées de deux manières différentes:
// With a ssortingng literal: let lines = read_file_lines("data.txt"); // With a ssortingng pointer: let file_name = ~"data.txt"; let lines = read_file_lines(file_name);
Ma première supposition était d’utiliser un pointeur emprunté ( &str
) pour le type de paramètre, mais quand cela ne fonctionnait pas (cela ne me permettait d’utiliser que @str
et ~str
), j’ai essayé ceci (en copiant les bibliothèques Rust), qui a fonctionné.
fn read_file_lines(path: &'a str) -> ~[~str] { let read_result = file_reader(~Path(path)); match read_result { Ok(file) => file.read_lines(), Err(e) => fail!(fmt!("Error reading file: %?", e)) } }
Le problème est que je ne comprends pas ce que je fais. D’après ce que je peux rassembler (principalement à partir d’erreurs de compilation), je déclare une durée de vie sans ressortingction et je l’utilise pour décrire le paramètre path (ce qui signifie que toute durée de vie peut être passée en paramètre).
Alors:
&str
et un paramètre de type &'a str
dans l’exemple ci-dessus? (J’utilise Rust 0.7 si cela fait une différence dans la réponse)
Mise à jour 2015-05-16 : le code dans la question d’origine s’appliquait à une ancienne version de Rust, mais les concepts restnt les mêmes. Cette réponse a été mise à jour pour utiliser les syntaxes / bibliothèques modernes de Rust. (Changer essentiellement ~[]
à Vec
et ~str
à Ssortingng
et ajuster l’exemple de code à la fin.)
Est-ce que ma compréhension est vaguement exacte?
[…]
Quelle est la différence entre un paramètre de type & str et un paramètre de type & ‘a str dans l’exemple ci-dessus?
Oui, une vie comme celle-là dit essentiellement “pas de ressortingctions”, en quelque sorte. Les vies sont un moyen de connecter des valeurs de sortie avec des entrées, par exemple fn foo<'a, T>(t: &'a T) -> &'a T
dit que foo
renvoie un pointeur qui a la même durée de vie que t
, , les données sur lesquelles il pointe sont valables pour la même durée que t
(enfin, ssortingctement, au moins aussi longtemps que). Cela implique essentiellement que la valeur de retour pointe sur une partie de la mémoire sur laquelle t
pointe.
Ainsi, une fonction comme fn<'a>(path: &'a str) -> Vec
est très similaire à l’écriture { let x = 1; return 2; }
{ let x = 1; return 2; }
{ let x = 1; return 2; }
… c’est une variable inutilisée.
Rust atsortingbue des durées de vie par défaut lors de l’écriture de &str
, et cela équivaut exactement à écrire la durée de vie de la variable inutilisée. ie fn(path: &str) -> Vec
n’est pas différent de la version avec 'a
s. Le seul moment d’abandon d’une durée de vie est différent de l’inclure si vous devez appliquer un pointeur global (c’est-à-dire la durée de vie 'static
spéciale) ou si vous souhaitez renvoyer une référence (par exemple -> &str
) valeur de retour a une durée de vie (et cela doit être soit la durée de vie d’une ou de plusieurs entrées, soit 'static
).
Qu’est-ce qu’une vie? Où puis-je en apprendre plus à leur sujet?
Une durée de vie correspond à la durée d’existence des données sur lesquelles pointe un pointeur. Par exemple, une variable globale est garantie pour durer “pour toujours” (elle a donc la durée 'static
vie spéciale 'static
). Une bonne façon de les examiner est la suivante: les durées de vie connectent les données au cadre de la stack sur lequel leur propriétaire est placé; Une fois que le frame de stack est terminé, le propriétaire sort de la scope et les pointeurs vers / dans cette valeur / structure de données ne sont plus valides, et la durée de vie est un moyen pour le compilateur de raisonner à ce sujet. (Avec la vue cadre de la stack, c’est comme si @
avait un cadre de stack spécial associé à la tâche en cours, et les éléments static
un cadre de stack “global”).
Il y a aussi un chapitre du livre sur la vie , et cet aperçu (NB: le code est maintenant dépassé mais les concepts sont toujours d’actualité) est une petite démonstration de comment utiliser des durées de vie pour éviter d’avoir à copier / allouer garantie: pas de possibilité de balancer des pointeurs).
Et pendant que j’y suis, qu’est-ce que c’est?
Littéralement rien de spécial, certains endroits exigent que les types aient des durées de vie (par exemple, dans les définitions de struct / enum et dans les impl
), et actuellement 'self
et 'static
sont les seuls noms acceptés. 'static
pour les pointeurs globaux toujours valables 'self
pour quelque chose qui peut avoir n’importe quelle vie. C’est un bug qui appelle static
durée de vie (non static
) autre que self
une erreur.
Dans l’ensemble, j’écrirais cette fonction comme:
use std::fs::File; use std::io::prelude::*; use std::io::BufReader; use std::path::Path; fn read_file_lines(path: &Path) -> Vec { match File::open(path) { Ok(file) => { let read = BufReader::new(file); read.lines().map(|x| x.unwrap()).collect() } Err(e) => panic!("Error reading file: {}", e) } } fn main() { let lines = read_file_lines(Path::new("foo/bar.txt")); // do things with lines }