Skip to content

Instantly share code, notes, and snippets.

@BSN4
Created January 8, 2025 18:36
Show Gist options
  • Save BSN4/ad62f4e9f3fe0ef68f54835dcca5d366 to your computer and use it in GitHub Desktop.
Save BSN4/ad62f4e9f3fe0ef68f54835dcca5d366 to your computer and use it in GitHub Desktop.
Async Rust Practical Patterns and Gotchas

Async Rust Practical Patterns and Gotchas

Struct Patterns

// For shared state across tasks, these are your main tools:
struct MyService {
    // Immutable shared state
    config: Arc<Config>,
    // Mutable shared state
    state: Arc<Mutex<State>>,
}

// Usage example:
impl MyService {
    async fn process(&self) {
        let state = self.state.lock().await;
        // Work with state...
    }

    async fn spawn_worker(&self) {
        let state = self.state.clone();
        let config = self.config.clone();
        
        tokio::spawn(async move {
            // Work with state and config...
        });
    }
}

Common Gotchas

1. Task Cancellation

// ❌ BAD: Counter may never decrement if task is cancelled
async fn wrong_task(counter: Arc<AtomicUsize>) {
    counter.fetch_add(1, Ordering::Relaxed);
    do_something().await;
    counter.fetch_sub(1, Ordering::Relaxed); // Might never run!
}

// ✅ GOOD: Use drop guard
async fn correct_task(counter: Arc<AtomicUsize>) {
    counter.fetch_add(1, Ordering::Relaxed);
    let _guard = scopeguard::guard((), |_| {
        counter.fetch_sub(1, Ordering::Relaxed);
    });
    do_something().await;
}

2. Select Loop Future Recreation

// ❌ BAD: Creates new futures on every loop iteration
async fn wrong_select(mut rx: mpsc::Receiver<()>) {
    loop {
        select! {
            _ = rx.recv() => {},
            _ = tokio::time::sleep(Duration::from_secs(1)) => {},
        }
    }
}

// ✅ GOOD: Create futures once outside the loop
async fn correct_select(mut rx: mpsc::Receiver<()>) {
    let mut recv = rx.recv().fuse();
    let mut timer = tokio::time::sleep(Duration::from_secs(1)).fuse();
    pin_mut!(recv, timer);

    loop {
        select! {
            _ = &mut recv => {},
            _ = &mut timer => {},
        }
    }
}

3. Task Starvation

// ❌ BAD: Blocks accepting new connections
async fn wrong_server(listener: TcpListener) {
    loop {
        let (socket, _) = listener.accept().await;
        process_socket(socket).await; // Blocks accept
    }
}

// ✅ GOOD: Spawn tasks for concurrent processing
async fn correct_server(listener: TcpListener) {
    loop {
        let (socket, _) = listener.accept().await;
        tokio::spawn(async move {
            process_socket(socket).await;
        });
    }
}

4. CPU-Intensive Tasks

// ❌ BAD: Blocks the async runtime
async fn wrong_heavy_task() {
    let result = expensive_computation();
    use_result(result).await;
}

// ✅ GOOD: Use spawn_blocking
async fn correct_heavy_task() {
    let result = tokio::task::spawn_blocking(|| {
        expensive_computation()
    }).await.unwrap();
    use_result(result).await;
}

5. RAII Guards Across Await

// ❌ BAD: Holds pool connection during unrelated work
async fn wrong_pool_usage(pool: &Pool) {
    let conn = pool.get().await;
    do_db_work(&conn).await;
    do_unrelated_work().await; // Still holding connection!
    do_more_db_work(&conn).await;
}

// ✅ GOOD: Release resources when not needed
async fn correct_pool_usage(pool: &Pool) {
    // Scope 1
    {
        let conn = pool.get().await;
        do_db_work(&conn).await;
    } // Connection released here

    do_unrelated_work().await;

    // Scope 2
    {
        let conn = pool.get().await;
        do_more_db_work(&conn).await;
    }
}

Best Practices

  1. Always use spawn_blocking for CPU-intensive work
  2. Think about cancellation at every .await point
  3. Use drop guards for cleanup code
  4. Don't hold RAII guards across .await points
  5. Spawn tasks for independent concurrent work
  6. Create long-lived futures outside loops
  7. Use fuse() and pin_mut! for select loops
  8. Consider if you really need async at all - threads might be simpler!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment