Par contre, dans le dernier exemple, j'ai l'erreur suivante:
Tu as oublié l'inversion des lignes selected_share = Some(share); et share_size = share_array.len();. Il faut d'abord finir d'utiliser share_array avant d'en transférer la propriété vers selected_share.
Il faut aussi que je m’intéresse plus à la partie unsafe et au lifetime.
La partie « unsafe » n'est pas utile pour 95% de la programmation en Rust. Mieux vaut réserver ça aux experts du langage qui en ont vraiment besoin et qui savent s'en servir correctement (comme les contributeurs à std), parce que c'est compliqué. Écrire du Rust unsafe correct est bien plus difficile qu'écrire du C correct. Il faut une compréhension fine de la sémantique du langage. Armin Ronacher a écrit un post de blog que je trouve intéressant à ce sujet.
Les lifetimes, par contre, font partie des bases du langage.
Sur la partie lifetime, j'ai beau comprendre à quoi cela sert et comment ça marche en théorie, je ne suis pas encore à l'aise avec. (Quand je parlerai de le deviner automatiquement, je pensais à l’élision qui pour le coup n'est pas vraiment du devinage). Je vais creuser un peu plus cette partie.
Je ne sais pas si ça va t'aider, mais pour ma part j'ai compris comment fonctionne le système quand j'ai réalisé les choses suivantes, que je ne trouve pas très bien expliquées dans la plupart des tutoriels.
D'abord, tout le monde parle du borrow checker et comme c'est magique, mais ce n'est pas le borrow checker qui fait tout. Par exemple, cette fonction ne compile pas : fn<'a>(x: &'a str) -> &'static str { x }. Et heureusement qu'elle ne compile pas, vu qu'on ne peut pas prendre une référence valable seulement pendant une durée déterminée et la transformer impunément en une référence valable pour toute l'exécution du programme ! Mais ce n'est pas le borrow checker qui vérifie ça, c'est juste le système de typage. Tout ce que fait le borrow checker, c'est l'inférence des lifetimes implicites à l'intérieur d'une fonction. Il ne remplace pas le système de typage.
En fait, une bonne façon de comprendre les lifetimes, c'est de les voir comme des types fantômes. On peut imaginer une variante du langage qui aurait de l'héritage entre structs (à la programmation orientée objet), et où les références seraient implémentées comme ceci :
structRef<T,U>{// correspond à « &'T U »ptr: *U,// le pointeur vers la valeur de type Utoken: T,// un « jeton de validité » de type T}
Le code
fnconvert<'a,'b: 'a>(x: &'bstr)-> &'astr{x}
est traduit en quelque chose comme ceci dans ce langage imaginaire :
fnconvert<T,UsubtypeofT>(x: Ref<U,str>)-> Ref<T,str>{letRef{ptr,token}=x;Ref{ptr,// ptr est déjà de type *str, pas besoin de conversiontokenasT,// token est du type U, qui est sous-type de T, donc on peut le convertir en T}}
et le code
{// 'alets=String::from("foo");lets_ref=&s;// référence valable pour 'a{// 'blets_ref2=s_ref;// référence valable pour 'b}}
L'héritage entre structs n'existe pas, ma syntaxe struct ValidityB inherits ValidityA est imaginaire, mais tu vois l'idée. (En fait, le sous-typage existe en Rust uniquement à cause des lifetimes.)
Bref, fondamentalement, les lifetimes sont juste des paramètres de type comme les autres. En théorie, rien n'empêcherait que tous les lifetimes soient implémentés via des paramètres de type normaux. Le fait d'avoir une syntaxe différente est surtout une question de lisibilité.
[^] # Re: Gestion mémoire etc.
Posté par jeanas (site web personnel, Mastodon) . En réponse au journal PullRequest d'une application en Rust. Évalué à 5.
Tu as oublié l'inversion des lignes
selected_share = Some(share);etshare_size = share_array.len();. Il faut d'abord finir d'utilisershare_arrayavant d'en transférer la propriété versselected_share.La partie « unsafe » n'est pas utile pour 95% de la programmation en Rust. Mieux vaut réserver ça aux experts du langage qui en ont vraiment besoin et qui savent s'en servir correctement (comme les contributeurs à
std), parce que c'est compliqué. Écrire du Rust unsafe correct est bien plus difficile qu'écrire du C correct. Il faut une compréhension fine de la sémantique du langage. Armin Ronacher a écrit un post de blog que je trouve intéressant à ce sujet.Les lifetimes, par contre, font partie des bases du langage.
Je ne sais pas si ça va t'aider, mais pour ma part j'ai compris comment fonctionne le système quand j'ai réalisé les choses suivantes, que je ne trouve pas très bien expliquées dans la plupart des tutoriels.
D'abord, tout le monde parle du borrow checker et comme c'est magique, mais ce n'est pas le borrow checker qui fait tout. Par exemple, cette fonction ne compile pas :
fn<'a>(x: &'a str) -> &'static str { x }. Et heureusement qu'elle ne compile pas, vu qu'on ne peut pas prendre une référence valable seulement pendant une durée déterminée et la transformer impunément en une référence valable pour toute l'exécution du programme ! Mais ce n'est pas le borrow checker qui vérifie ça, c'est juste le système de typage. Tout ce que fait le borrow checker, c'est l'inférence des lifetimes implicites à l'intérieur d'une fonction. Il ne remplace pas le système de typage.En fait, une bonne façon de comprendre les lifetimes, c'est de les voir comme des types fantômes. On peut imaginer une variante du langage qui aurait de l'héritage entre structs (à la programmation orientée objet), et où les références seraient implémentées comme ceci :
Le code
est traduit en quelque chose comme ceci dans ce langage imaginaire :
et le code
se traduit par
L'héritage entre structs n'existe pas, ma syntaxe
struct ValidityB inherits ValidityAest imaginaire, mais tu vois l'idée. (En fait, le sous-typage existe en Rust uniquement à cause des lifetimes.)Bref, fondamentalement, les lifetimes sont juste des paramètres de type comme les autres. En théorie, rien n'empêcherait que tous les lifetimes soient implémentés via des paramètres de type normaux. Le fait d'avoir une syntaxe différente est surtout une question de lisibilité.