How to pass cli args value into a function that calls via tokio::spawn in parallel mode

Hi there, I'm writing a CLI tool to use in the security penetration area, my problem is that I don't know how to pass CLI arg to a function that is calling via tokio::async and concurrent mode.
my code is below:
line 23 is where I got the arg value
line 38 is where I attempt to use its value to pass to the target function

Error is:
args does not live long enough

borrowed value does not live long enoughrustc(E0597)

main.rs(24, 19): borrowed value does not live long enough

main.rs(44, 35): argument requires that args is borrowed for 'static

main.rs(57, 1): args dropped here while still borrowed

use regex::Regex;
use serde::*;
use std::any::Any;
use std::collections::*;
use std::env;
use std::sync::Arc;
use std::thread;
use std::time::Duration;

#[derive(Serialize, Deserialize)]
struct TimeRecord {
    timeStamp: String,
    url: String,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let args: Vec<String> = env::args().collect();
    if args.len() == 1 {
        panic!("Pass the domain name in the parameter!");
    }

    let domain = &args[1];

    let base_url = format!("https://web.archive.org/cdx/search/cdx?url={}/robots.txt&output=json&filter=statuscode:200&fl=timestamp,original&collapse=digest",&domain);
    let resp = reqwest::get(base_url).await?.text().await?;

    let json: Vec<TimeRecord> = serde_json::from_str(&resp).expect("JSON was not well-formatted");
    let mut handles = Vec::new();
    let mut result: HashMap<char, usize> = HashMap::new();

    for obj in json {
        if obj.url == "original" {
            continue;
        }

        let handle = tokio::spawn(async {
            get_robot_file(domain, obj.timeStamp, obj.url).await;
        });
        handles.push(handle);
    }

    for handle in handles {
        let map = handle.await.unwrap();
    }

    Ok(())
}

async fn get_robot_file(
    base_domain: &String,
    timestamp: String,
    url: String,
) -> Result<String, Box<dyn std::error::Error>> {
    let base_url = format!("http://web.archive.org/web/{}if_/{}", timestamp, url);

    let resp = reqwest::get(base_url).await?.text().await?;

    if resp.is_empty() {
        return Ok(String::from("ok"));
    }

    let items: Vec<&str> = resp.trim().split("\n").collect();

    for item in items {
        if item.to_lowercase().contains("disallow:") || item.to_lowercase().contains("allow:") {
            let sub_item: Vec<&str> = item.split(": ").collect();
            if sub_item.capacity() <= 1 {
                continue;
            }

            let temp = &sub_item[1]
                .replace("\n", "")
                .replace("*", "")
                .replace("\r", "");

            if !temp.is_empty() {
                let mut result = String::new();
                result.push_str(base_domain);
                result.push_str(temp);
                println!("{:#?}", &result);
            }
        }
    }

    Ok(String::from("ok"))
}

Looking at the documentation for tokio::spawn(), you'll find the type bound T: Future + Send + 'static. The 'static part means that the async block inside of it cannot reference any borrowed data. Instead, you must clone domain in order to move it into the block:

        let domain = domain.clone();
        let handle = tokio::spawn(async move {
            get_robot_file(&domain, obj.timeStamp, obj.url).await;
        });
        handles.push(handle);

Also, later in the file, you download from the URL http://web.archive.org/web/{}if_/{}. I'd instead recommend using https://web.archive.org/web/{}id_/{}, since it retrieves the file exactly as downloaded.

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.