CodeBlanks Logo Icon CodeBlanks

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.

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

main.rs
use std::cell::RefCell;

fn main() {
    // buraya yaz
}

Sample solution

main.rs
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

Question
fn bildir(&self, mesaj: &str) {
    self.mesajlar.____().push(mesaj.to_string());
}
Answer
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)

All lessons in Intermediate