Непонятная ошибка в Rust с временем жизни объекта trait
Может кто-нибудь сказать, в чем проблема со следующим кодом? Компилятор жалуется на время жизни, но сообщение об ошибке не имеет абсолютно никакого смысла. Я перепробовал все, что мог придумать, но ничего не помогло.
use std::borrow::BorrowMut;
trait Trait<'a> {
fn accept(&mut self, &'a u8);
}
struct Impl<'a>{
myref: Option<&'a u8>,
}
impl<'a> Trait<'a> for Impl<'a> {
fn accept(&mut self, inp: &'a u8) { self.myref = Some(inp); }
}
fn new<'a>() -> Box<Trait<'a> + 'a> {
Box::new(Impl{myref: None})
}
fn user<'a>(obj: &mut Trait<'a>) {}
fn parent<'a>(x: &'a u8) {
let mut pool = new();
user(pool.borrow_mut());
}
Ошибка компилятора
error: `pool` does not live long enough
--> src/wtf.rs:22:10
|
22 | user(pool.borrow_mut());
| ^^^^ does not live long enough
23 | }
| - borrowed value dropped before borrower
|
= note: values in a scope are dropped in the opposite order they are created
Что не имеет абсолютно никакого смысла. Как заемщик переживает что-нибудь? Я даже не использую заемную стоимость!
3 ответа
Хорошо, это имеет смысл, но это трудно увидеть из-за жизненного выбора. Итак, вот ваш код со всеми жизнями, записанными в явном виде, и с несущественными подробностями:
use std::borrow::BorrowMut;
trait Trait<'a> {}
struct Impl<'a> {
myref: Option<&'a u8>,
}
impl<'a> Trait<'a> for Impl<'a> {}
fn new<'a>() -> Box<Trait<'a> + 'a> {
Box::new(Impl { myref: None })
}
fn user<'a, 'b>(obj: &'b mut (Trait<'a> + 'b)) {}
fn parent() {
/* 'i: */ let mut pool/*: Box<Trait<'x> + 'x>*/ = new();
/* 'j: */ let pool_ref/*: &'i mut Box<Trait<'x> + 'x>*/ = &mut pool;
/* BorrowMut<T>::borrow_mut<'d>(&'d mut Self) -> &'d mut T */
/* 'k: */ let pool_borrow/*: &'i mut (Trait<'x> + 'x)*/ = Box::borrow_mut(pool_ref);
user(pool_borrow);
}
Теперь с точки зрения последней строки parent
мы можем выработать следующие эквивалентности, просто прочитав определение user
и подставляя времена жизни, которые мы имеем в parent
:
'a
знак равно'x
'b
знак равно'i
'b
знак равно'x
Кроме того, это позволяет сделать вывод, что:
'x
знак равно'i
Это проблема. Из-за того, как вы определили user
Вы оказались в ситуации, когда жизнь pool_ref
заимствовать (который равен времени жизни pool
место хранения, которое вы заимствуете) должно быть таким же, как и срок службы 'x
используется в вещи, хранящейся в pool
,
Это немного похоже на Box
возможность иметь указатель на себя, прежде чем он существует, что не имеет никакого смысла.
В любом случае, исправить это просто. + Изменить user
на самом деле иметь правильный тип:
fn user<'a, 'b>(obj: &'b mut (Trait<'a> + 'a)) {}
Это соответствует типу, произведенному new
, Альтернативно, просто не используйтеborrow_mut
:
user(&mut *pool)
Это работает, потому что это "заимствование". призвание borrow_mut
переводит время жизни более или менее напрямую, но повторное заимствование позволяет компилятору сузить заимствования до более короткого времени жизни. Другими словами, явно borrow_mut
не дает компилятору достаточной свободы, чтобы "выдумать" времена жизни, чтобы они все выстраивались в линию, что делает повторное заимствование.
Как быстро:
Я даже не использую заемную стоимость!
Ненужные. Rust проверяет тип и время жизни полностью локально. Он никогда не смотрит на тело другой функции, чтобы увидеть, что он делает; это идет на интерфейсе в одиночку. Компилятор не проверяет и не заботится о том, что вы делаете внутри другой функции.
Обратите внимание, что есть еще сообщение об ошибке:
error: `pool` does not live long enough
--> src/main.rs:25:10
|>
25 |> user(pool.borrow_mut());
|> ^^^^
note: reference must be valid for the block at 23:25...
--> src/main.rs:23:26
|>
23 |> fn parent<'a>(x: &'a u8) {
|> ^
note: ...but borrowed value is only valid for the block suffix following statement 0 at 24:25
--> src/main.rs:24:26
|>
24 |> let mut pool = new();
|> ^
Давайте посмотрим на user
:
fn user<'a>(obj: &mut Trait<'a>) {}
Это говорит о том, что он примет изменяемую ссылку (с неназванным временем жизни) на объект признака, параметризованный с временем жизни 'a
,
Поворачиваясь к new
Я бы сказал, что метод очень подозрительный:
fn new<'a>() -> Box<Trait<'a> + 'a> {
Box::new(Impl { myref: None })
}
Это говорит о том, что он вернет объект в штучной упаковке с любым временем жизни, указанным вызывающей стороной. Это в принципе никогда не имеет смысла.
Все, что сказал, я не ясно, почему код выбирает для использования borrow_mut
, Я бы написал это более прямо:
user(&mut *pool);
Это разыменовывает Box<Trait>
чтобы получить Trait
, затем принимает изменчивую ссылку, давая &mut Trait
, который компилирует.
Я не могу сейчас объяснить, почему BorrowMut
отличается по поведению.
Я не уверен, почему эта ошибка происходит, но я могу дать решения!
Во-первых, кажется, что с помощью borrow_mut
излишне ограничивает время жизни возвращаемой ссылки. Использование операторов для создания ссылки решает ошибку.
fn parent() {
let mut pool = new();
user(&mut *pool);
}
Однако, если мы этого не сделаем, мы можем устранить ошибку, добавив время жизни, привязанное к Trait
объект в user
"s obj
аргумент.
fn user<'a>(obj: &mut (Trait<'a> + 'a)) {}