Hyper, Warp, and 'static App state

I'm having trouble convincing either Hyper or Warp request handlers to accept some borrowed data from main. I have tried a number of formations, but they all look something like:

struct Foo {
    thing: String,
}

struct Bar<'a> {
    stuff: &'a str,
}

fn main() {
    let foo = Foo {
        thing: "thing".to_string(),
    };
    let bar = Bar {
        stuff: foo.thing.as_str(),
    };
    work(&bar);
}

#[tokio::main]
async fn work(bar: &Bar<'_>) -> Result<(), Infallible> {
    let ad = SocketAddr::from(([127, 0, 0, 1], 3030));
    let sv = make_service_fn(move |_| {
        // MAGIC in which `hello_world` is called
    });
    Server::bind(&ad).serve(sv).await?;
    Ok(())
}

async fn hello_world(bar: &Bar<'_>, _: Request<Body>) -> Result<Response<Body>, Infallible> {
    let msg = format!("Hello, World: {}", bar.stuff);
    Ok(Response::new(msg.into()))
}

I have followed the official state example as best I could for Hyper, and tried similar things for Warp to no avail. In the end it boils down to the borrow checker complaining that (using the symbol names above) that foo does not live long enough when borrowed into bar, claiming it needs to be 'static. Not sure what else to tell Rust at this point, because it looks pretty darn static to me! main owns it after all!

In other projects I've been able to pass vanilla &'d things into async blocks just fine (i.e. without Arc etc.), so long as the calling block owned the underlying data, as it does here. So what's the difference? How do I convince these libraries to accept my borrowed data?

Thank you kindly.

P.S. I realized that #[tokio::main] is usually put on main, but it doesn't have to be. I did it this way to prove to myself (and you) that main absolutely owns the underlying data.

But compiler can't (and won't) prove that hello_world can't be called after main returns, since make_service_fn reserves the right to call the provided closure whenever it wants. The fact that the owner is main is irrelevant, since from borrow checker point of view main is not special at all.

And this isn't even a limitation of the borrow checker; it reflects actual possibilities during execution:

  • main() drops its variables as it returns, and that process could involve a &'static reference, so it would be incorrect to count main()'s locals as static.

  • main() can be called from another function. This is usually not a sensible thing to do, but it can be — in which case its variables are just as temporary as any other function's.

So my assumptions about main's contents being auto-'static were incorrect. Okay, I'll accept that. But is my original ask possible? Can borrowed data be sent to these handlers? I suppose not, if they're demanding 'static. I may have to pull some Arc tricks after all.

I'm looking at this Warp example now, but it seems to me that if I really want my A-borrows-from-B setup, and B is the original data source, then I'll need self-references, which aren't possible (yet).

What I'm really trying to do in my real code is have a Vec<T> as the underlying source of truth, with a number of HashMap<K, &T> acting as kinds of hand-rolled DB indices to allow for faster lookups. It is these HashMaps that I want the handlers to have access to. Morally this should be possible, but I haven't yet discovered the correct incantation.

If you want 'static things you don't absolutely need Arc for them — you can use Box::leak() inside your main() to make a Box<T> into an &'static mut T, which you can then coerce to &'static T to make shareable. This is, of course, a memory leak if you misuse it for repeated allocation, but for things that are genuinely going to be used as long as the process exists, it's cheaper than Arc.

1 Like

It would only be a single allocation, living forever in main , so perhaps this is the way forward.

It works...! My requests are going through and accessing my custom indices...! :exploding_head:

Box::leak... I have learned a dangerous thing this day :laughing: Thank you.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.