Компилятор предполагает, что я добавляю статическое время жизни, потому что тип параметра может не прожить достаточно долго, но я не думаю, что то, что я хочу
Извините за неопределенный заголовок, но я довольно новичок в Rust, поэтому я точно не знаю, как кратко подвести итог моей проблеме.
Я пытаюсь реализовать что-то, что (показывая минимальный пример) выглядит следующим образом:
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));
}
}
Так как Rust по умолчанию (насколько я могу судить, по-русски), моя ментальная модель думает, что это должно работать. Метод add принимает собственность на объект x
, а затем способен переместить этот объект в Box
, потому что он знает полный тип U
(а не только символ Bar<T>
). Когда он перемещается в ящик, время жизни элемента внутри поля должно быть привязано к фактическому времени жизни блока (например, когда pop()
отброшен вектор, объект будет уничтожен).
Ясно, однако, что компилятор не согласен (и я уверен, что знает немного больше, чем я...), прося меня рассмотреть вопрос о добавлении классификатора времени жизни 'static
(E0310). Я на 99% уверен, что не то, что хочу, но я не совсем уверен, что я должен делать.
Чтобы прояснить, что я думаю, и помочь определить неправильные представления, моя модель по существу (я исхожу из фона С++):
-
Box<T>
по существу std::unique_ptr<T>
- Без каких-либо аннотаций переменные передаются по значению, если
Copy
и rvalue-reference в противном случае
- Со ссылкой на аннотацию
&
примерно const&
и &mut
примерно &
- Время жизни по умолчанию - лексическая область
Ответы
Ответ 1
Проверьте всю ошибку:
error[E0310]: the parameter type `U` may not live long enough
--> src/main.rs:9:24
|
9 | self.data.push(Box::new(x));
| ^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `U: 'static`...
note: ...so that the type `U` will meet its required lifetime bounds
--> src/main.rs:9:24
|
9 | self.data.push(Box::new(x));
| ^^^^^^^^^^^
В частности, компилятор позволяет вам знать, что возможно, что какой-то произвольный тип U
может содержать ссылку, и эта ссылка может стать недействительной:
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());
}
}
Это будет плохая новость.
Если вы хотите, чтобы время жизни 'static
или параметризованное время жизни соответствовало вашим потребностям. Время жизни 'static
проще в использовании, но имеет больше ограничений. Из-за этого это значение по умолчанию, когда вы объявляете объект-признак в структуре или псевдониме типа:
struct Foo<T> {
data: Vec<Box<Bar<T>>>,
// same as
// data: Vec<Box<Bar<T> + 'static>>,
}
Однако при использовании в качестве аргумента объект-признак использует пожизненный elision и получает уникальное время жизни:
fn foo(&self, x: Box<Bar<T>>)
// same as
// fn foo<'a, 'b>(&'a self, x: Box<Bar<T> + 'b>)
Эти две вещи должны совпадать.
struct Foo<'a, T> {
data: Vec<Box<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));
}
}
или
struct Foo<T> {
data: Vec<Box<Bar<T>>>
}
impl<T> Foo<T> {
fn add<U>(&mut self, x: U)
where U: Bar<T> + 'static
{
self.data.push(Box::new(x));
}
}
Ответ 2
попросив меня рассмотреть вопрос о добавлении "статического классификатора времени жизни" (E0310). Я на 99% уверен, что не то, что хочу, но я не совсем уверен, что я должен делать.
Да, это так. Компилятору не нужна ссылка &'static
, она хочет U: 'static
.
Имея U: 'static
, означает, что U
не содержит ссылок со временем жизни меньше 'static
. Это необходимо, потому что вы хотите поместить экземпляр U
в структуру без времени жизни.
trait Bar<T> {}
struct Foo<T> {
data: Vec<Box<Bar<T>>>
}
impl<T> Foo<T> {
fn add<U: Bar<T> + 'static>(&mut self, x: U) {
self.data.push(Box::new(x));
}
}