Usage of self.clone() in async fn and alternatives

Hello everyone.

I'm a Rust beginner and I'm trying to rewrite a load testing tool I wrote for work in Rust.
I finally got the code to compile and ran cargo clippy on it, but it feels forced and wrong. What bothers me most is the fact that I'm using self.clone() on every iteration.

I would be grateful for any kind of feedback and a push in the right direction.

#[derive(Clone)]
pub struct Target {
    pub url: String,
    pub method: String,
    pub headers: header::HeaderMap,
    pub body: String,
    pub cert: Option<PathBuf>,
    pub key: Option<PathBuf>,
    pub root_ca: Option<PathBuf>,
    pub timeout: Duration,
}

impl Target {
    pub async fn stress(
        &self,
        n_requests: usize,
        time: Duration,
    ) -> (Arc<AtomicUsize>, Arc<AtomicUsize>) {
        let start = Instant::now();
        let client = Arc::new(self.create_client());
        let r_count: Arc<AtomicUsize> = Arc::new(AtomicUsize::new(0));
        let e_count: Arc<AtomicUsize> = Arc::new(AtomicUsize::new(0));
        while start.elapsed() < time {
            let mut tasks = Vec::new();
            for _ in 0..n_requests {
                let client_clone = client.clone();
                let counter = r_count.clone();
                let errors = e_count.clone();
                let self_clone = self.clone();
                tasks.push(tokio::spawn(async move {
                    let res = client_clone
                        .request(
                            Method::from_bytes(self_clone.method.as_bytes()).unwrap(),
                            self_clone.url.as_str(),
                        )
                        .body(self_clone.body)
                        .send()
                        .await;
                    match res {
                        Ok(_) => counter.fetch_add(1, Ordering::SeqCst),
                        Err(_) => errors.fetch_add(1, Ordering::SeqCst),
                    }
                }));
            }
            join_all(tasks).await;
            sleep(Duration::from_secs(1));
        }
        (r_count, e_count)
    }
}

You can wrap types in Arc to allow sharing them cheaply. Also, you are using a non-async sleep in an async function, which is a bad idea, as it doesn't yield control back to the executor during that sleep, meaning that no other spawned tasks can run on that thread during the sleep. You'll want to use delay_for instead.

If you make a function call that isn't instant, and also doesn't have an .await, then you are blocking the executor, and should try to find an async version, or at least run it inside an spawn_blocking block.

Thank you very much for your quick helpful reply.
That is just what I needed to know.

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