#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));
}
}