Less awkward use of RefCell


#1

Several times that I have tried using a RefCell, I’ve found the code to be complicated. One of my use cases has to do with a cached value in a struct that otherwise behaves as immutable. I was wondering if there is a more idiomatic way of handling something like the frobnicate function like in this example:
RefCell example

I think my specific instance, it’s easier to just require &mut self, and make things easier, but it still be nice to know if I’m missing a clearer way to deal with the RefCell.


#2

Well, in this case, you could use Cell:

use std::cell::Cell;

fn main() {
    let item = Thing::new();

    item.frobnicate(1);
    println!("{:?}", item.inside);
}

struct Thing {
    inside: Cell<Option<u32>>,
}

impl Thing {
    fn new() -> Thing {
        Thing {
            inside: Cell::new(None),
        }
    }

    fn frobnicate(&self, amount: u32) {
        let mut inside = self.inside.get()
            .unwrap_or_else(|| {
                let init = 0;
                init
            });

        inside += amount;
        
        self.inside.set(Some(inside));
    }
}

Also, I’m not really sure why you keep re-borrowing inside; just do a single borrow_mut at the start, then work on that. You don’t have to switch between borrow and borrow_mut like that.

Edit: For completeness, here’s a simpler one using RefCell:

use std::cell::RefCell;

fn main() {
    let item = Thing::new();

    item.frobnicate(1);
    println!("{:?}", item.inside);
}

struct Thing {
    inside: RefCell<Option<u32>>,
}

impl Thing {
    fn new() -> Thing {
        Thing {
            inside: RefCell::new(None),
        }
    }

    fn frobnicate(&self, amount: u32) {
        let mut inside = self.inside.borrow_mut();
        
        if !inside.is_some() {
            let init = 0;
            *inside = Some(init);
        }
        
        let inside = inside.as_mut().unwrap();
        
        *inside += amount;
    }
}