DEV Community

Franz Wong
Franz Wong

Posted on

Runtime check on borrowing rule with RefCell

Rust does compile time check on borrowing rule. But there are some scenarios we need to defer it to runtime. Let me show a simplified version from one of the examples in the book

We have a trait with one method. No change on internal states is allowed inside this method. (&self is used instead of &mut self)

pub trait Messenger {
    fn send(&self, msg: &str);
}
Enter fullscreen mode Exit fullscreen mode

What if we want to store the messages sent? (e.g. it's common when we create a mock object for testing) It means we need to change the internal states. For some reasons (e.g. it is from 3rd party), we can't modify this trait, so we can't change to &mut self. Anyway, let's do it first and see what will happen.

struct MyMessenger {
    messages: Vec<String>
}

impl MyMessenger {
    fn new() -> MyMessenger {
        MyMessenger { messages: vec![] }
    }
}

impl Messenger for MyMessenger {
    fn send(&self, msg: &str) {
        self.messages.push(String::from(msg));
    }
}
Enter fullscreen mode Exit fullscreen mode

On the line push method is called, the compiler complains.

cannot borrow `self.messages` as mutable, as it is behind a `&` reference

`self` is a `&` reference, so the data it refers to cannot be borrowed as mutablerustcClick for full compiler diagnostic

main.rs(2, 13): consider changing that to be a mutable reference: `&mut self`
Enter fullscreen mode Exit fullscreen mode

We want to make it passes the compilation. Let's rewrite it with RefCell.

use std::cell::RefCell;

struct MyMessenger {
    messages: RefCell<Vec<String>>
}

impl MyMessenger {
    fn new() -> MyMessenger {
        MyMessenger { messages: RefCell::new(vec![]) }
    }
}

impl Messenger for MyMessenger {
    fn send(&self, msg: &str) {
        self.messages.borrow_mut().push(String::from(msg));
    }
}
Enter fullscreen mode Exit fullscreen mode

We use borrow_mut to borrow a mutable value. Compilation passes this time.

The borrowing rule is still checked in runtime. Runtime error will occur if we do this.

(This violates the rule "At any given time, we can have either one mutable reference or any number of immutable references".)

let x = RefCell::new(String::from("Hello World"));
let y = x.borrow_mut();
let z = x.borrow(); // Error occurs here in runtime
Enter fullscreen mode Exit fullscreen mode

Top comments (0)