How to Use RwLock<T> in Rust

Use `RwLock<T>` when you need to share data across multiple threads where reads are frequent but writes are rare, as it allows unlimited concurrent readers or a single exclusive writer.

Use RwLock<T> when you need to share data across multiple threads where reads are frequent but writes are rare, as it allows unlimited concurrent readers or a single exclusive writer. Unlike Mutex, which blocks all access during a write, RwLock maximizes read throughput by permitting simultaneous read locks while ensuring write locks remain exclusive.

You acquire a read lock with .read() for shared access or a write lock with .write() for exclusive access. Both methods return a guard object that automatically releases the lock when dropped, preventing deadlocks if you forget to unlock manually. Be aware that if a thread holds a write lock, all other threads (readers and writers) will block until it is released; conversely, if a thread holds a read lock, writers will block until all readers finish.

Here is a practical example of a thread-safe counter that supports concurrent reads:

use std::sync::{Arc, RwLock};
use std::thread;

fn main() {
    // Wrap the data in Arc for shared ownership and RwLock for interior mutability
    let counter = Arc::new(RwLock::new(0));

    let mut handles = vec![];

    // Spawn 5 threads that only read the value
    for _ in 0..5 {
        let counter_clone = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            // Acquire a read lock (multiple threads can hold this simultaneously)
            let val = *counter_clone.read().unwrap();
            println!("Read value: {}", val);
        });
        handles.push(handle);
    }

    // Spawn 1 thread that writes to the value
    let counter_clone = Arc::clone(&counter);
    let handle = thread::spawn(move || {
        // Acquire a write lock (blocks until all readers are done)
        let mut val = counter_clone.write().unwrap();
        *val += 1;
        println!("Updated value to: {}", *val);
    });
    handles.push(handle);

    for handle in handles {
        handle.join().unwrap();
    }
}

For scenarios involving heavy write contention, RwLock can suffer from writer starvation or lock contention overhead, potentially performing worse than a simple Mutex. In those cases, consider using tokio::sync::RwLock for async runtimes or re-architecting to reduce shared state. Always handle the Result returned by .read() and .write() (usually via .unwrap() in examples, but proper error handling is required in production) to manage potential poisoning if a thread panics while holding the lock.