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>
Copy
et rvalue-reference sinon&
Est à peu près const&
Et &mut
Est à peu près &
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));
}
}
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));
}
}