Les durées de vie : quelle solution intelligente ?

Le problème exposé dans ce sujet a été résolu.

EDIT
Je précise, parce qu’au vu des réponses, cela n’est pas aussi évident que je le pensais : ma question s’adresse à ceux qui connaissent le Rust, puisqu’elle porte sur une bonne pratique spécifique à ce langage. Une réponse généraliste n’apportera pas de solution à mon problème. Merci de votre compréhension.

Salut à tous.

Je joue à essayer d’adapter des fonctions de la bibliothèque standard du Haskell en Rust, pour tester les limites de l’aspect fonctionnel de ce dernier, et je me suis retrouvé face à un obstacle inattendu.

La fonction Haskell que je cherche à adapter est la suivante.

1
2
fromMaybe     :: a -> Maybe a -> a
fromMaybe d x = case x of {Nothing -> d; Just v  -> v}

C’est très bateau : on fournit une valeur par défaut d et une valeur Maybe (le type Option de Rust), et si elle vaut Nothing (None en Rust), la fonction renvoie d, si elle vaut Just v (Option(v) en Rust), la fonction renvoie v.

Transposé en Rust, ça donne ça.

1
2
3
4
5
6
fn fromMaybe<A> (d : &A, x : &Maybe<A>) -> &A   {
    match *x {
        Maybe::Nothing => d,
        Maybe::Just(ref v) => v
    }
}

À la compilation, j’obtiens l’erreur suivante.

1
2
3
4
5
maybe.rs:31:44: 31:46 error: missing lifetime specifier [E0106]
maybe.rs:31 fn fromMaybe<A> (d : &A, x : &Maybe<A>) -> &A   {
                                                       ^~
maybe.rs:31:44: 31:46 help: run `rustc --explain E0106` to see a detailed explanation
maybe.rs:31:44: 31:46 help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `d` or `x`

Ce qui est parfaitement logique quand on y réfléchit. Ma question ne porte donc pas sur la raison de cette erreur, mais serait plutôt : quelle serait la solution intelligente à ce problème ?

J’ai bien pensé à rajouter une durée de vie identique pour les deux paramètres et la sortie, et ça compile effectivement, mais a priori, rien ne me permet vraiment d’affirmer que les deux paramètres donnés en entrée auront la même durée de vie. Est-ce que je passe à côté de quelque chose ?

+1 -0

Ah je vois la confusion. Les références de Rust sont plus que les références du C++. Si je passe les arguments par valeur, j’en transfère la propriété à la fonction, ce qui a pour conséquence que je n’ai plus le droit de m’en servir dans la suite du code appelant.

Imagine le code suivant.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
fn isNothing<A> (m : Maybe<A>) -> bool  {
    match m {
        Maybe::Nothing => true,
        _ => false
    }
}

fn fromMaybe<A> (d : A, x : Maybe<A>) -> A   {
    match x {
        Maybe::Nothing => d,
        Maybe::Just(v) => v
    }
}

fn main()   {
    let just35 = Maybe::Just(35.0);
    let float35 = fromMaybe(12.0, just35);
    let nope = isNothing(just35);
}

La dernière ligne génère l’erreur suivante.

1
2
valeur.rs:47:26: 47:32 error: use of moved value: `just35` [E0382]
valeur.rs:47     let nope = isNothing(just35);

Et ce parce que la ligne d’avant a pris la propriété sur just35. D’où la nécessité de passer par références.

+0 -0

Ta fonction peut sortir doit d soit v. il est donc nécessaire qu'ils aient la même durée de vie, ca fait parti des pré conditions de ta fonction. Si tu ne peux pas le formaliser en Rust (aucune idée si tu peux), ainsi soit il, il y'a toujours des choses qu'un langage ne peut pas exprimer de façon formelle .

+0 -0

C'est un problème d'élision des durées de vie. Ta fonction a pour signature

1
fn fromMaybe<A> (d : &A, x : &Maybe<A>) -> &A

Ce qui correspond pour le compilateur à

1
fn fromMaybe<A> (d : &'a A, x : &'b Maybe<A>) -> &'c A

Le problème est que le compilateur ne peut pas associer simplement ces durées de vie. Ici, il te faut indiquer explicitement que toutes tes références ont la même durée de vie:

1
2
3
4
5
6
fn fromMaybe<A> (d : &'a A, x : &'a Maybe<A>) -> &'a A   {
    match x {
        Maybe::Nothing => d,
        Maybe::Just(v) => &v
    }
}

Avec ce code on obtient une erreur car le pattern essaye de prendre possession de v:

1
maybe.rs:9:28: 9:29 error: `v` does not live long enough

Il suffit d'ajouter une capture par référence dans le pattern:

1
2
3
4
5
6
fn fromMaybe<A> (d : &'a A, x : &'a Maybe<A>) -> &'a A   {
    match x {
        Maybe::Nothing => d,
        Maybe::Just(ref v) => v
    }
}

Ton code est l'un des cas où les règles d'élision ne suffisent pas à supprimer l’ambiguïté. La doc qui correspond aux élisions de durée de vie: http://doc.rust-lang.org/book/lifetimes.html#lifetime-elision

+0 -0

Le fin de cette réponse sur Stack Overflow semble indiquer que si la même durée de vie est assignée aux entrées et à la sortie, ça ne veut pas dire que les références doivent toutes avoir la même durée de vie, mais que la valeur de retour aura la plus petite durée de vie parmis les entrées.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte