Rust
External mutation
Mutate inner data inside an immutable structure.
Mutation from outside
If a trait method takes &self and your implementation needs to keep state (for example, recording messages sent in a test), you can't change the trait signature. This is where interior mutability comes in:
trait Bildirici {
fn bildir(&self, mesaj: &str);
}
struct Kayitci {
mesajlar: RefCell<Vec<String>>,
}
impl Bildirici for Kayitci {
fn bildir(&self, mesaj: &str) {
self.mesajlar.borrow_mut().push(mesaj.to_string());
}
}
The using code
The code using the notifier knows only the trait and takes &dyn Bildirici:
fn kota_denetle(b: &dyn Bildirici, yuzde: u32) {
if yuzde > 90 {
b.bildir(&format!("uyarı: {yuzde}"));
}
}
let k = Kayitci { mesajlar: RefCell::new(Vec::new()) };
kota_denetle(&k, 95);
println!("{}", k.mesajlar.borrow().len()); // 1
In the real application Bildirici sends an email; in tests Kayitci only stores the messages.
Exercise Recording notifier
Write the Bildirici, Kayitci and kota_denetle code above. Call it with 95, 50 and 99 in order; print the number of records and the first message separated by a space.
Show example solution
use std::cell::RefCell;
trait Bildirici {
fn bildir(&self, mesaj: &str);
}
struct Kayitci {
mesajlar: RefCell<Vec<String>>,
}
impl Bildirici for Kayitci {
fn bildir(&self, mesaj: &str) {
self.mesajlar.borrow_mut().push(mesaj.to_string());
}
}
fn kota_denetle(b: &dyn Bildirici, yuzde: u32) {
if yuzde > 90 {
b.bildir(&format!("uyarı: {yuzde}"));
}
}
fn main() {
let k = Kayitci { mesajlar: RefCell::new(Vec::new()) };
for y in [95, 50, 99] {
kota_denetle(&k, y);
}
let m = k.mesajlar.borrow();
println!("{} {}", m.len(), m[0]);
}
Test yourself
-
Where is this pattern used most?
- In constants
- In test mocks and structures keeping hidden state, like caches
- In every struct field
- In thread pools
Show answer
In test mocks and structures keeping hidden state, like caches — It suits structures that look immutable from outside but keep records inside.
-
Store the record
fn bildir(&self, mesaj: &str) {
self.mesajlar.____().push(mesaj.to_string());
}
Show answer
fn bildir(&self, mesaj: &str) {
self.mesajlar.borrow_mut().push(mesaj.to_string());
}
-
Call and record count
- kota_denetle(&k, 95)
- +1
- kota_denetle(&k, 50)
- Unchanged
- kota_denetle(&k, 91)
- +1 (above the limit)
- kota_denetle(&k, 90)
- Unchanged (at the limit)
Next lesson: Summary - Interior mutability