web-dev-qa-db-fra.com

Le compilateur suggère d'ajouter une 'durée de vie statique car le type de paramètre peut ne pas vivre assez longtemps, mais je ne pense pas que c'est ce que je veux

J'essaie d'implémenter quelque chose qui ressemble à cet exemple minimal:

trait Bar<T> {}

struct Foo<T> {
    data: Vec<Box<Bar<T>>>,
}

impl<T> Foo<T> {
    fn add<U: Bar<T>>(&mut self, x: U) {
        self.data.Push(Box::new(x));
    }
}

Depuis Rust par défaut (pour autant que je sache) pass-by-owner, mon modèle mental pense que cela devrait fonctionner. La méthode add prend possession de l'objet x et est capable de déplacer cet objet dans un Box car il connaît le type complet U (et pas seulement le trait Bar<T>). Une fois déplacé dans un Box, la durée de vie de l'élément à l'intérieur de la boîte doit être liée à la durée de vie réelle de la boîte (par exemple, lorsque pop() ed hors du vecteur, l'objet sera détruit).

De toute évidence, cependant, le compilateur n'est pas d'accord (et je suis sûr qu'il en sait un peu plus que moi ...), me demandant d'envisager d'ajouter un qualificatif de durée de vie 'static (E0310). Je suis sûr à 99% que ce n'est pas ce que je veux, mais je ne suis pas exactement sûr de ce que je suis censé faire.

Pour clarifier ce que je pense et aider à identifier les idées fausses, mon modèle mental, issu d'un contexte C++, est le suivant:

  • Box<T> Est essentiellement std::unique_ptr<T>
  • Sans aucune annotation, les variables sont passées par valeur si Copy et rvalue-reference sinon
  • Avec une annotation de référence, & Est à peu près const& Et &mut Est à peu près &
  • La durée de vie par défaut est la portée lexicale
34
Robert Mason

Découvrez toute l'erreur:

error[E0310]: the parameter type `U` may not live long enough
 --> src/main.rs:9:24
  |
8 |     fn add<U: Bar<T>>(&mut self, x: U) {
  |            -- help: consider adding an explicit lifetime bound `U: 'static`...
9 |         self.data.Push(Box::new(x));
  |                        ^^^^^^^^^^^
  |
note: ...so that the type `U` will meet its required lifetime bounds
 --> src/main.rs:9:24
  |
9 |         self.data.Push(Box::new(x));
  |                        ^^^^^^^^^^^

Plus précisément, le compilateur vous fait savoir qu'il est possible qu'un type arbitraire U puisse contenir une référence , et cette référence pourrait alors devenir invalide:

impl<'a, T> Bar<T> for &'a str {}

fn main() {
    let mut foo = Foo { data: vec![] };

    {
        let s = "oh no".to_string();
        foo.add(s.as_ref());
    }
}

Ce serait une mauvaise nouvelle.

Que vous souhaitiez un 'static la durée de vie ou une durée de vie paramétrée dépend de vos besoins. Le 'static la durée de vie est plus facile à utiliser, mais comporte plus de restrictions. Pour cette raison, c'est la valeur par défaut lorsque vous déclarez un objet trait dans une structure ou un alias de type:

struct Foo<T> {
    data: Vec<Box<dyn Bar<T>>>,
    // same as
    // data: Vec<Box<dyn Bar<T> + 'static>>,
} 

Cependant, lorsqu'il est utilisé comme argument, un objet trait utilise l'élision de durée de vie et obtient une durée de vie unique:

fn foo(&self, x: Box<dyn Bar<T>>)
// same as
// fn foo<'a, 'b>(&'a self, x: Box<dyn Bar<T> + 'b>)

Ces deux choses doivent correspondre.

struct Foo<'a, T> {
    data: Vec<Box<dyn Bar<T> + 'a>>,
}

impl<'a, T> Foo<'a, T> {
    fn add<U>(&mut self, x: U)
    where
        U: Bar<T> + 'a,
    {
        self.data.Push(Box::new(x));
    }
}

o

struct Foo<T> {
    data: Vec<Box<dyn Bar<T>>>,
}

impl<T> Foo<T> {
    fn add<U>(&mut self, x: U)
    where
        U: Bar<T> + 'static,
    {
        self.data.Push(Box::new(x));
    }
}
34
Shepmaster

me demandant d'envisager d'ajouter un "qualificatif de durée de vie statique (E0310). Je suis sûr à 99% que ce n'est pas ce que je veux, mais je ne suis pas exactement sûr de ce que je suis censé faire.

Oui, ça l'est. Le compilateur ne veut pas de &'static référence, il veut U: 'static.

Ayant U: 'static signifie que U ne contient aucune référence avec une durée de vie inférieure à 'static. Ceci est nécessaire car vous souhaitez placer une instance U dans une structure sans durée de vie.

trait Bar<T> {}

struct Foo<T> {
    data: Vec<Box<dyn Bar<T>>>,
}

impl<T> Foo<T> {
    fn add<U: Bar<T> + 'static>(&mut self, x: U) {
        self.data.Push(Box::new(x));
    }
}
17
mcarton