El compilador sugiere que agregue una "vida útil estática" porque es posible que el tipo de parámetro no dure lo suficiente, pero no creo que eso sea lo que quiero.
Estoy intentando implementar algo parecido a este ejemplo mínimo:
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));
}
}
Dado que Rust por defecto (hasta donde yo sé) pasa por propiedad, mi modelo mental cree que esto debería funcionar. El add
método toma posesión del objeto x
y puede mover este objeto a Box
porque conoce el tipo completo U
(y no solo el rasgo Bar<T>
). Una vez trasladado a Box
, la vida útil del artículo dentro de la caja debe estar vinculada a la vida útil real de la caja (por ejemplo, cuandopop()
se elimine el vector, el objeto será destruido).
Sin embargo, claramente el compilador no está de acuerdo (y estoy seguro de que sabe un poco más que yo...) y me pide que considere agregar un'static
calificador de por vida (E0310). Estoy 99% seguro de que eso no es lo que quiero, pero no estoy exactamente seguro de lo que se supone que debo hacer.
Para aclarar lo que estoy pensando y ayudar a identificar conceptos erróneos, mi modelo mental, procedente de C++, es:
Box<T>
Es esencialstd::unique_ptr<T>
- Sin ninguna anotación, las variables se pasan por valor si
Copy
y rvalue-reference en caso contrario - Con una anotación de referencia,
&
es aproximadamenteconst&
y&mut
es aproximadamente&
- La vida útil predeterminada es el alcance léxico.
Mira el error completo:
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));
| ^^^^^^^^^^^
Específicamente, el compilador le informa que es posible que algún tipo arbitrario U
contenga una referencia , y esa referencia podría dejar de ser válida:
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());
}
}
Serían malas noticias.
Si desea una 'static
vida útil o una vida útil parametrizada, depende de sus necesidades. La 'static
vida útil es más fácil de usar, pero tiene más restricciones. Debido a esto, es el valor predeterminado cuando declaras un objeto de rasgo en una estructura o un alias de tipo:
struct Foo<T> {
data: Vec<Box<dyn Bar<T>>>,
// same as
// data: Vec<Box<dyn Bar<T> + 'static>>,
}
Sin embargo, cuando se utiliza como argumento, un objeto de rasgo utiliza la elisión de duración y obtiene una duración única:
fn foo(&self, x: Box<dyn Bar<T>>)
// same as
// fn foo<'a, 'b>(&'a self, x: Box<dyn Bar<T> + 'b>)
Estas dos cosas deben coincidir.
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));
}
}
pidiéndome que considere agregar un 'calificador de por vida estático (E0310). Estoy 99% seguro de que eso no es lo que quiero, pero no estoy exactamente seguro de lo que se supone que debo hacer.
Sí, lo es. El compilador no quiere una &'static
referencia, quiere U: 'static
.
Tener U: 'static
significa que U
no contiene referencias con una vida útil inferior a 'static
. Esto es necesario porque desea colocar una U
instancia en una estructura sin duración.
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));
}
}