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


#1

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

  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 требует именно его.

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


#2

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

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

#3

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


#4

Option::take()


#5

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

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`

#6

А просто с borrow

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


#7
let sender = RefCell::new(Some(sender));
                          ^^^^

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


#8

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

    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:


#9

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


#10

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

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

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


#11

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


#12

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


#13

Верно :vulcan_salute:


#14

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