How to move a variable into async closure with tokio

I am trying to set a variable based on CLI option and then move it to async closure. But borrow-checker says the variable does not live long enough, seems due to async closure. Here is the code:

#[tokio::main]
async fn main() {
    let matches = clap::App::new("MyApp")
        .version("0.1.0")
        .arg(
            Arg::with_name("root")
                .short("r")
                .long("root")
                .takes_value(true)
                .help("Root directory"),
        )
        .get_matches();

    let root_dir = matches.value_of("root").unwrap_or("/home");

    // A `Service` is needed for every connection
    let make_svc = make_service_fn(|_conn| async move {
        Ok::<_, Error>(service_fn(move |req| async move {
            hello_world(req, root_dir).await
        }))
    });

Note that root_dir type is &static str, but because it's moved into async closure, borrow-checker deems it not living long enough. Any way to get around it? (I am trying to avoid create String` and change hello_world , unless that's the only way.)

And note that if I hard-code root_dir (not get it from CLI), it will work:

let root_dir = "/home";

Even though the type is the same (&'static str) . Why?

It's because root_dir is not &'static str. It might be a &str, but it isn't a static one.

VS code tells it's &'static str. I will try to use rust reflection to verify (don't know how to do it yet).

Rust doesn't have reflection. Just check the error if you do this:

let root_dir: &'static str = matches.value_of("root").unwrap_or("/home");

I think you are right. I don't know why VS code shows that. I will change to String. Thanks.

While I'm still struggling to get the code working, can I ask what is the idiomatic way of doing something like this in Rust?

i.e. in the above case, I wanted to capture a &str or String into the async closure.

Finally I found a solution creating a String, using Arc and called .clone() twice, without changing hello_world signature. It's like this:

    let root_dir = Arc::new(String::from(matches.value_of("root").unwrap_or("/home")));

    // A `Service` is needed for every connection
    let make_svc = make_service_fn(move |_conn| {
        let root_dir = root_dir.clone();
        async move {
            Ok::<_, Error>(service_fn(move |req| {
                let root_dir = root_dir.clone();
                async move {
                    hello_world(req, root_dir.as_str()).await
                }
            }))
        }
    });

If there is a better way doing this, please let me know. Thanks!

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.