Course contentsShow
← Rust
Lesson 717 of 1,9106. Generics and Static DispatchFree lesson

Generics and Interior Mutability

Implement thread-safe containers that handle interior mutability through generic wrappers.

In this lesson, you will learn how to create generic, thread-safe containers that allow you to modify data even when the container itself is held behind an immutable reference.

The Problem: Mutability vs. Sharing

Normally, Rust’s ownership rules strictly enforce that you can have either many immutable references (&T) or one mutable reference (&mut T). However, sometimes you need to share data across multiple threads while still being able to update the inner value. This is where "Interior Mutability" types like Mutex<T> come into play.

By wrapping our data in a generic struct, we can provide a reusable interface that handles the locking logic for any type T that implements Send.

Implementing a Generic Thread-Safe Container

Imagine we want a universal "LockedValue" wrapper. We can define this using a generic type parameter T and leverage std::sync::Mutex to manage the interior mutability:

use std::sync::Mutex;

struct LockedValue<T> {
    inner: Mutex<T>,
}

impl<T> LockedValue<T> {
    pub fn new(value: T) -> Self {
        Self { inner: Mutex::new(value) }
    }

    pub fn update<F>(&self, op: F) 
    where 
        F: FnOnce(&mut T) 
    {
        // We lock the mutex to gain interior mutability
        let mut data = self.inner.lock().unwrap();
        op(&mut data);
    }
}

Why this matters

In the code above, the update method takes a generic closure F. This allows the user of the struct to define how the data changes without the struct itself needing to know the specific type or the specific mutation logic.

Even if you have an immutable reference to LockedValue<T>, calling update allows you to safely transition the interior state. Because Mutex handles the synchronization, this design is thread-safe, making your generic container highly reusable across concurrent parts of your application. By combining generics with interior mutability, you decouple the mechanism of safe access from the actual data being stored.

Key Takeaway: You can build flexible, thread-safe abstractions by wrapping generic types in synchronization primitives, allowing you to modify inner data even when the container is shared immutably.