Rust book - RefCell
Interior Mutability: mutate a variable when there are immutable references to that data inside an unsafe block, and wrap it in a safe API and the outer type is still immutable
RefCell enforces the borrow checker’s rules at runtime, whilst Box enforces at compile time
Read about “Halting Problem”
Designed to be used in single threaded scenarios
The interior mutability pattern can be used during tests, when a struct/functions takes in a immutable struct/reference. We can mutate the reference internally, but still pass as immutable reference.
trait MessageDispatcher {
fn dispatch(&self, message: String);
}
struct MessageBuilder<'a, T: MessageDispatcher> {
dispatcher: &'a T,
}
impl<'a, T> MessageBuilder<'a, T>
where
T: MessageDispatcher,
{
fn new(dispatcher: &'a T) -> Self {
Self { dispatcher }
}
}
impl<'a, T> MessageBuilder<'a, T>
where
T: MessageDispatcher,
{
fn build_and_send_message(&self, name: &str) {
let message = format!("Hello, {name}");
self.dispatcher.dispatch(message);
}
}
#[cfg(test)]
mod test {
use std::{
borrow::{Borrow, BorrowMut},
cell::RefCell,
};
use super::{MessageBuilder, MessageDispatcher};
struct Messenger {
message: RefCell<String>,
}
impl Messenger {
fn new() -> Self {
Self {
message: RefCell::new(String::from("")),
}
}
}
impl MessageDispatcher for Messenger {
fn dispatch(&self, m: String) {
let mut message = self.message.borrow_mut();
*message = m;
}
}
#[test]
fn test_build_message() {
let messenger = Messenger::new();
let builder = MessageBuilder::new(&messenger);
builder.build_and_send_message("Ian");
assert_eq!(messenger.message.into_inner(), "Hello, Ian");
}
}
RefCell keeps track of the number of mutable and immutable references are active, in order to mimic the borrow checker, but in run-time. Same rules applies:
- One mutable reference
- Many immutable references
RefCell can be combined with Rc to create a mutable value with multiple owners