Совместное использование структур данных при асинхронном закрытии

Я застрял, пытаясь разделить (только для чтения) структуры данных между асинхронными помощниками. Я пытаюсь создать Hyper-сервер, на котором я предварительно генерирую некоторые данные, которые могут использоваться всеми обработчиками запросов.

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

#[tokio::main]
async fn main() {
    let address = SocketAddr::from(([127, 0, 0, 1], 3000));

    let pages = generate_static_pages();

    let make_service = make_service_fn(|_conn| async move {
        Ok::<_, Infallible>(service_fn(|req: Request<Body>| async move {
            serve(pages, req)
        }))
    });

    let server = Server::bind(&address).serve(make_service);

    if let Err(error) = server.await {
        eprintln!("server error: {}", error);
    }
}

В моем случае generate_static_pages() возвращает HashMap<&'static str, Bytes> с предварительно созданными страницами. К сожалению, эту хеш-карту нельзя сгенерировать во время компиляции, потому что это значительно упростит задачу. Теперь я борюсь, потому что pages не может быть заимствован закрытием: "не может выйти из pages, захваченной переменной в FnMut замыкании"

Я попытался передать ссылку, но это не сработало, потому что Rust не может сделать вывод, что переменная живет достаточно долго, чтобы можно было использовать замыкание. Затем я попытался использовать .clone(), но это не сработало, потому что он будет вызываться для переменной после ее перемещения, а это невозможно. Наконец, я попытался обернуть Arc, но это не решило проблему, в основном по той же причине.

Что бы вы посоветовали мне сделать? Спасибо!


person arendjr    schedule 07.06.2020    source источник


Ответы (1)


Если вам нужны только неизменяемые ссылки на страницы, вы можете использовать ящик lazy_static. lazy_static позволяет инициализировать статические переменные во время выполнения - это очень полезно!

Ваш код будет выглядеть примерно так:

use lazy_static::lazy_static;

lazy_static! {
  static ref PAGES: HashMap<&'static str, Bytes> = generate_static_pages();
}

#[tokio::main]
async fn main() {
    let address = SocketAddr::from(([127, 0, 0, 1], 3000));

    let make_service = make_service_fn(|_conn| async move {
        Ok::<_, Infallible>(service_fn(|req: Request<Body>| async move {
            serve(&PAGES, req)
        }))
    });

    let server = Server::bind(&address).serve(make_service);

    if let Err(error) = server.await {
        eprintln!("server error: {}", error);
    }
}

Кроме того, вот еще один lazy_static пример.

person Bennett Hardwick    schedule 07.06.2020
comment
Из любопытства, как мне решить эту проблему, если мне нужен изменяемый доступ? Ваше здоровье! - person arendjr; 07.06.2020
comment
Использование Arc<Mutex> на каждом узле или на всей карте или использование параллельной хэш-карты (например, evmap) - person Asya Corbeau; 07.06.2020
comment
Как сказал @AsyaCorbeau, чтобы обеспечить внутреннюю изменчивость, вам нужно будет использовать Mutex или RefCell. Вы можете поместить его в Arc или использовать lazy_static. Вот пример использования lazy_static: ссылка на игровую площадку - person Bennett Hardwick; 07.06.2020
comment
Однако будьте осторожны при использовании мьютексов, потому что вы можете столкнуться с проблемой, называемой взаимоблокировкой. - person Bennett Hardwick; 07.06.2020