2
votes

I'm stuck trying to share (read-only) data structures across async helpers. What I'm trying to accomplish is create a Hyper server where I pre-generate some data that can be used by all request handlers.

Here is the example from the Hyper getting started guide, extended with what I'm trying to do:

#[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);
    }
}

In my case, generate_static_pages() returns a HashMap<&'static str, Bytes> with pre-generated pages. Unfortunately, this hash map cannot be generated at compile-time, because that would make things a lot easier. Now, I struggle because pages cannot be borrowed by the closures: "cannot move out of pages, a captured variable in an FnMut closure"

I tried to pass a reference, but that didn't work because Rust cannot infer the variable to live long enough for the closure to use. I then tried to use .clone() but that doesn't work because it would be called on the variable after it is moved, which it can't. Finally, I tried wrapping in an Arc, but that doesn't solve it, basically because of the same reason.

What would you advice me to do? Thanks!

1

1 Answers

1
votes

If you only want immutable references to the pages then you should be able to use the lazy_static crate. lazy_static lets you initialize static variables at runtime - it's quite useful!

Your code would end up looking something like:

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

Also, here's another lazy_static example.