2016-09-06 4 views
6

Может ли кто-нибудь сказать, в чем проблема с приведенным ниже кодом? Компилятор жалуется на время жизни, но сообщение об ошибке не имеет абсолютно никакого смысла. Я пробовал все, что мог придумать, но ничего не помогает.Ошибка при запуске в ржавчине с пожизненным ресурсом объекта

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 

Который не имеет абсолютно никакого смысла. Как заемщик переживает что-нибудь? Я даже не использую заемную ценность!

ответ

10

Хорошо, это делает имеет смысл, но это трудно понять, из-за пожизненной элизии.Итак, вот ваш код с всего времени жизнь выписана в явном виде, и незначащими детали забит:

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) 

This работает, потому что это "повторное заимствование". Вызов borrow_mut переводит время жизни более или менее напрямую, но повторное заимствование позволяет компилятору сузить заготовки до более короткого срока службы. Иными словами, явное обращение к borrow_mut не позволяет компилятору достаточно свободы для «выдумывания» сроков жизни, чтобы сделать их все линейными, повторное заимствование делает.

Как быстро в сторону:

Я даже не используя заимствованные значение!

Неприменимо. Rust не проверяет тип и срок службы полностью локально. Это never смотрит на тело другой функции, чтобы увидеть, что он делает; он идет только по интерфейсу. Компилятор не проверяет и не заботится о том, что вы делаете внутри другая функция.

+0

Откуда берется ограничение '' b = 'x'? – Antimony

+0

Извините, я случайно скопировал + вставил уже установленную версию примера! Чтобы уточнить, '& 'i mut (Trait <'x> +' x)' (аргумент) унифицирован с символом '& 'b mut (Trait <'a> +' b)' (подпись 'user', до фиксации). Это означает, что '' b' унифицирован * как *, так и '' x'. –

+0

О, это имеет смысл! Я полагаю, что новый, вероятно, должен вернуть что-то вроде Trait <'a> + 'b, но компилятор жаловался, когда у меня не было + a. – Antimony

4

Обратите внимание, что есть больше, чтобы сообщение об ошибке:

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

Это говорит о том, что он будет возвращать объект в штучной упаковке черта с независимо от времени жизни абонент указывает. That basically never makes sense.

Все, что сказал, я не понимаю, почему код выбирает использовать borrow_mut. Я бы написал, что более непосредственно:

user(&mut *pool); 

Это разыменовывает Box<Trait> получить Trait, затем принимает изменяемую ссылку, получая &mut Trait, который компилирует.

В настоящее время я не могу объяснить, почему BorrowMut отличается поведением.

+0

Как вы передадите правильное время жизни новому в этом случае? Я предположил, что компилятор автоматически выбирает правильный. Если я попытаюсь передать явное время жизни в, это дает ошибку в передаче слишком большого количества параметров времени жизни. Постскриптум Я отправил полное сообщение об ошибке, которое я видел. Я не знаю, почему компилятор дает вам более длинное сообщение об ошибке. – Antimony

+0

@ Антитея, в которую вы не проходите; это всегда выводится из кода. В этом случае существует только одно возможное время жизни - '' static', поэтому метод должен, вероятно, быть 'fn new() -> Box + 'static>' (который I * believe * совпадает с 'fn new () -> Вставка '). – Shepmaster

+0

Но это не имеет смысла, потому что возвращенный объект фактически не будет иметь статического времени жизни. Или это просто косвенно сужается позже? – Antimony

2

Я не уверен почему эта ошибка случается, но я могу дать решения!

Во-первых, кажется, что использование borrow_mut излишне ограничивает срок службы возвращаемой ссылки. Использование операторов для создания ссылки решает ошибку.

fn parent() { 
    let mut pool = new(); 
    user(&mut *pool); 
} 

Однако, если мы не сделать это, мы можем решить эту ошибку, добавив жизни, связанные с Trait объекта в user «s obj аргумента.

fn user<'a>(obj: &mut (Trait<'a> + 'a)) {}