In this lesson, you will learn how to encapsulate UnsafeCell and other interior mutability primitives behind a clean, panic-free, and sound public API.
When you use UnsafeCell, you are effectively taking manual control over the borrow checker. If you expose raw pointers or permit arbitrary modifications from your public interface, you risk creating data races or undefined behavior for the user of your library. To keep your code safe, your module must act as a "gatekeeper" that hides the mutable state entirely.
Designing the Encapsulation
The goal is to ensure that while your internal implementation uses UnsafeCell to perform "impossible" mutations (like mutating a value through an immutable reference), the external consumer interacts only with high-level, idiomatic Rust methods.
Consider a simple shared cache that allows internal updates:
use std::cell::UnsafeCell;
pub struct SharedCache<T> {
data: UnsafeCell<T>,
}
impl<T> SharedCache<T> {
pub fn new(val: T) -> Self {
Self { data: UnsafeCell::new(val) }
}
// We hide the UnsafeCell behind a controlled update method.
pub fn update(&self, new_val: T) {
unsafe {
*self.data.get() = new_val;
}
}
pub fn get(&self) -> &T {
unsafe { &*self.data.get() }
}
}
In this example, the user of SharedCache never sees an UnsafeCell. They cannot manually access the underlying pointer. By restricting the API to update and get, we ensure that the logic governing how the data is modified remains under our control.
Why this matters
By forcing all mutations through specific methods, you maintain the "single source of truth" for the object's safety. You can add runtime checks, logging, or state validation inside update() without changing the public contract. The user doesn't need to know that you are using UnsafeCell; they only need to know that your methods obey the standard rules of shared access. Your code becomes a "black box" that provides safe functionality while secretly leveraging the power of interior mutability.
Key Takeaway: Always wrap UnsafeCell in a private field and expose only safe, well-defined methods to ensure the public API remains sound and ergonomic.