Компилятор предполагает, что я добавляю статическое время жизни, потому что тип параметра может не прожить достаточно долго, но я не думаю, что то, что я хочу

Извините за неопределенный заголовок, но я довольно новичок в 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));
    }
}