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

#rust #lifetime

#Ржавчина #время жизни

Вопрос:

Я пытаюсь реализовать что-то похожее на этот минимальный пример:

 trait Bar<T> {}

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

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

Поскольку в Rust по умолчанию используется (насколько я могу судить) передача права собственности, моя ментальная модель считает, что это должно сработать. add Метод становится владельцем object x и может перемещать этот объект в a Box , потому что он знает полный тип U (а не только признак Bar<T> ). После перемещения в a Box время жизни элемента внутри поля должно быть привязано к фактическому времени жизни поля (например, при pop() удалении вектора объект будет уничтожен).

Однако очевидно, что компилятор не согласен (и я уверен, что знает немного больше, чем я …), прося меня рассмотреть возможность добавления квалификатора 'static времени жизни (E0310). Я на 99% уверен, что это не то, чего я хочу, но я не совсем уверен, что я должен делать.

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

  • Box<T> по сути std::unique_ptr<T>
  • Без каких-либо аннотаций переменные передаются по значению, если Copy и rvalue-ссылка в противном случае
  • Со ссылочной аннотацией amp; примерно constamp; и amp;mut примерно amp;
  • Время жизни по умолчанию — лексическая область

Ответ №1:

Проверьте всю ошибку:

 error[E0310]: the parameter type `U` may not live long enough
 --> src/main.rs:9:24
  |
8 |     fn add<U: Bar<T>>(amp;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));
  |                        ^^^^^^^^^^^
 

В частности, компилятор сообщает вам, что возможно, что некоторый произвольный тип U может содержать ссылку, и эта ссылка может стать недействительной:

 impl<'a, T> Bar<T> for amp;'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<dyn Bar<T>>>,
    // same as
    // data: Vec<Box<dyn Bar<T>   'static>>,
} 
 

Однако при использовании в качестве аргумента объект признака использует исключение времени жизни и получает уникальное время жизни:

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

Эти две вещи должны совпадать.

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

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

или

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

impl<T> Foo<T> {
    fn add<U>(amp;mut self, x: U)
    where
        U: Bar<T>   'static,
    {
        self.data.push(Box::new(x));
    }
}
 

Комментарии:

1. Я не могу выразить, насколько ценно это объяснение. Большое вам спасибо!

Ответ №2:

прошу меня рассмотреть возможность добавления «статического времени жизни» (E0310). Я на 99% уверен, что это не то, чего я хочу, но я не совсем уверен, что я должен делать.

Да, это так. Компилятору не нужна amp;'static ссылка, он хочет U: 'static .

Наличие U: 'static означает, что U не содержит ссылок с временем жизни меньше 'static . Это необходимо, потому что вы хотите поместить U экземпляр в структуру без времени жизни.

 trait Bar<T> {}

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

impl<T> Foo<T> {
    fn add<U: Bar<T>   'static>(amp;mut self, x: U) {
        self.data.push(Box::new(x));
    }
}