How to Implement a Rate Limiter in Async Rust

Implement an async rate limiter in Rust using a Mutex-protected HashMap to track request timestamps per client within a time window.

Use the tokio runtime with std::sync::Mutex and std::collections::HashMap to track request timestamps per client. This approach blocks the async task briefly to check the limit, then allows the request to proceed if under the threshold.

use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::time::{SystemTime, UNIX_EPOCH};

struct RateLimiter {
    requests: Mutex<HashMap<String, Vec<u64>>>,
    max_requests: usize,
    window_seconds: u64,
}

impl RateLimiter {
    fn new(max_requests: usize, window_seconds: u64) -> Self {
        Self {
            requests: Mutex::new(HashMap::new()),
            max_requests,
            window_seconds,
        }
    }

    fn is_allowed(&self, client_id: &str) -> bool {
        let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
        let mut map = self.requests.lock().unwrap();
        let entry = map.entry(client_id.to_string()).or_insert_with(Vec::new);
        entry.retain(|&t| now - t < self.window_seconds);
        let allowed = entry.len() < self.max_requests;
        if allowed {
            entry.push(now);
        }
        allowed
    }
}

#[tokio::main]
async fn main() {
    let limiter = Arc::new(RateLimiter::new(5, 60));
    let client_id = "user_123";
    if limiter.is_allowed(client_id) {
        println!("Request allowed");
    } else {
        println!("Rate limit exceeded");
    }
}