Непонятная ошибка в 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)) {}
Другие вопросы по тегам