External mutation
- Rust course
- Intermediate
- Lesson 40/115
- 4 min
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()); // 1In the real application Bildirici sends an email; in tests Kayitci only stores the messages.
Practice 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.
Starter code
use std::cell::RefCell;
fn main() {
// buraya yaz
}Sample 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]);
}Expected output
2 uyarı: 95
Questions and answers
Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.
1 Where is this pattern used most?
- In constants
- In test mocks and structures keeping hidden state, like caches Correct answer
- In every struct field
- In thread pools
Explanation: It suits structures that look immutable from outside but keep records inside.
2 Store the record
fn bildir(&self, mesaj: &str) {
self.mesajlar.____().push(mesaj.to_string());
}
fn bildir(&self, mesaj: &str) {
self.mesajlar.borrow_mut().push(mesaj.to_string());
}
Explanation: Pushing to the vector behind &self needs borrow_mut.
3 Call and record count
| Term | Meaning |
|---|---|
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) |