Заимствование в замыкании hyper

Тех.вопросы
Mar 14, 2019
N

Здравствуйте!
Натолкнулся на первую серьезную проблему с заимствованием. Задача - нужно, чтобы веб-сервер обработал один запрос и завершился. Код такой

  let addr: SocketAddr = ([127, 0, 0, 1], 3000).into();
    let (sender, receiver) = futures::sync::oneshot::channel::<()>();
    let proc = || {
        let res = service_fn_ok(got_google_response);
        sender.send(());
        res
    };

    let server = Server::bind(&addr)
        .serve(proc)
        .with_graceful_shutdown(receiver)
        .map_err(|e| eprintln!("server error: {}", e));

Rust компилировать отказывается т.к. serve требует Fn, но proc имеет тип FnOnce из-за того, что метод send принимает объект sender во владение.
Сделать канал не oneshot тоже не выходит, т.к. with_graceful_shutdown требует именно его.

Как обойти данную проблему? Есть какой-то подход?
Спасибо.

I

В принципе можно так сделать:

    let (sender, receiver) = futures::sync::oneshot::channel::<()>();
    let sender = RefCell::new(Some(sender));
    let proc = || {
        let res = service_fn_ok(got_google_response);
        sender.borrow_mut().take().map(|s| s.send(()));
        res
    };
N
Replying to #2

А take() - это чей метод? Что-то не могу его найти ни в Sender ни в Arc.

I
Replying to #3

Option::take()

N
Replying to #4

Компилятор тоже на это ругается

no method named `take` found for type `&mut auth::futures::Sender<()>` in the current scope

И c map - та же история. Получилось вот так

sender.borrow_mut().send(());

Но теперь компилятор ругается

this closure implements `FnMut`, not `Fn`
N

А просто с borrow

   |
59 |         sender.borrow().send(());
   |         ^^^^^^^^^^^^^^^ cannot move out of borrowed content

I
Replying to #5
let sender = RefCell::new(Some(sender));
                          ^^^^

Внутри RefCell должен быть Option<Sender>.

N

Получилось! Откомпилировалось, по крайней мере. Вот окончательный вариант.

    let addr: SocketAddr = ([127, 0, 0, 1], 3000).into();
    let (sender, receiver) = futures::sync::oneshot::channel::<()>();
    let sender = RefCell::new(Some(sender));
    let proc = move || {
        let res = service_fn_ok(got_google_response);
        sender.borrow_mut().take().unwrap().send(());
        res
    };

Сейчас попытаюсь это осознать :slight_smile:

I
Replying to #8

Тут лучше вместо unwrap() сделать как я выше писал. Можно панику словить если следующий пакет придет до того как сервер остановится.

N
Replying to #8

Не пойму, зачем тут Option. Т.е. да, без него не компилируется и понятно почему.

   |
61 |         sender.borrow_mut().send(());
   |         ^^^^^^^^^^^^^^^^^^^ cannot move out of borrowed content

Но как Option решает эту проблему?

I
Replying to #10

Нельзя переместить заимствованные данные. Option остается заимствованным, но при этом позволяет переместить то, что лежит у него внутри с помощью take().

N
Replying to #11

Т.е., мой вариант кода упадёт при повторном вызове proc, т.к. в Option уже будет не Some, а None? Другими словами, здесь Option используется как контейнер для транспортировки значения?

I
Replying to #12

Верно :vulcan_salute:

N

Большое спасибо! :grinning: