Выполнение асинхронных изменяемых операций с фьючерсами Rust - PullRequest
0 голосов
/ 17 мая 2018

Я создаю сервис в Rust, используя tokio-rs, и до сих пор был доволен этим техническим стеком. Сейчас я пытаюсь объединить асинхронные операции, которые включают в себя запись и затрудняются с проверкой заимствований.

Мой упрощенный пример минимального кода такой:

extern crate futures; // 0.1.21

use futures::Future;
use std::{cell::RefCell, rc::Rc};

trait RequestProcessor {
    fn prepare(&self) -> Box<Future<Item = (), Error = ()>>;
    fn process(&mut self, request: String) -> Box<Future<Item = (), Error = ()>>;
}

struct Service {
    processor: Rc<RefCell<RequestProcessor>>,
}

impl Service {
    fn serve(&mut self, request: String) -> Box<Future<Item = (), Error = ()>> {
        let processor_clone = self.processor.clone();
        let result_fut = self
            .processor
            .borrow()
            .prepare()
            .and_then(move |_| processor_clone.borrow_mut().process(request));
        Box::new(result_fut)
    }
}

fn main() {}

Как краткое резюме, после шага подготовки к асинхронности я пытаюсь запустить другую асинхронную операцию, которая записывает поле self. Без изменчивости это легко работает с простым членом Rc, но изменчивость нарушает его, вызывая следующую ошибку:

error[E0597]: `processor_clone` does not live long enough
  --> src/main.rs:22:32
   |
22 |             .and_then(move |_| processor_clone.borrow_mut().process(request));
   |                                ^^^^^^^^^^^^^^^                             - `processor_clone` dropped here while still borrowed
   |                                |
   |                                borrowed value does not live long enough
   |
   = note: values in a scope are dropped in the opposite order they are created

Я ожидаю, что это должно сработать, я не вижу, где по-прежнему заимствована изменяемая ссылка. Я думаю, что process() должен освободить процессор &mut self после того, как будет возвращено будущее, поэтому ошибка компиляции не должна возникать.

Не могли бы вы объяснить основные причины? Как изменить этот пример, чтобы он был принят компилятором?

1 Ответ

0 голосов
/ 17 мая 2018

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

.and_then(move |_| {
    let c = processor_clone;
    let mut d = c.borrow_mut();
    let e = d.process(request);
    e
});

Если вы скомпилируете это ... это работает.Если мы попробуем рекомбинировать строки, мы можем получить это с ошибкой:

.and_then(move |_| {
    let c = processor_clone;
    c.borrow_mut().process(request)
});

И это сработает:

.and_then(move |_| {
    let c = processor_clone;
    return c.borrow_mut().process(request);
});

Единственная разница - явный возврат и точка с запятой.Это очень похоже на При возврате результата использования StdinLock, почему был сохранен заем для stdin? , поэтому давайте попробуем предложить один из ответов, чтобы включить нелексическийвремя жизни .Это также позволяет компилировать ваш исходный код.

TL; DR: Это слабое место в текущей реализации средства проверки заимствований в Rust, и в какой-то момент оно будет волшебным образом исправлено.А пока я предлагаю написать в виде двух строк:

.and_then(move |_| {
    let mut c = processor_clone.borrow_mut();
    c.process(request)
});
...