Hi, I'm attempting to make a special RefCell that can be unborrowed temporarily. An explanation is provided in the code comments. A Playground link is provided below.
use std::cell::{RefCell, RefMut};
// Here's the situation:
//
// I'm trying to make a special version of "RefCell". Let's call it StuffCell.
//
// StuffCell is similar to RefCell, except:
// - Access is protected by a token called "Gold", which you must give in order
// to borrow the Stuff.
// - You can temporarily unborrow the Stuff through a method called
// unborrow_then, which takes a closure. The Gold is passed to the closure and
// can be used within the closure.
struct Stuff { }
/// Gold is basically a precious resource that cannot be forged and is
/// carefully managed by the library.
/// In order to borrow Stuff, you have to give up your Gold.
/// The "Gold" is innaccessible while StuffCell is borrowed.
/// To get the Gold back, you can either drop the borrow
/// or access it within unborrow_then.
struct Gold { } // No Copy or Clone for you, bucko!
struct StuffCell(RefCell<Stuff>);
impl StuffCell {
fn borrow_mut<'a>(&'a self, gold: &'a mut Gold) -> BorrowedStuff<'a> {
BorrowedStuff::Borrowed {
ref_mut: self.0.borrow_mut(),
cell: self,
gold,
}
}
}
enum BorrowedStuff<'a> {
Borrowed {
ref_mut: RefMut<'a, Stuff>,
cell: &'a StuffCell,
gold: &'a mut Gold,
},
Unborrowed,
}
impl BorrowedStuff<'_> {
fn unborrow_then<'a: 'b, 'b, F, T>(&'a mut self, f: F) -> T
where
F: FnOnce(&'b mut Gold) -> T
{
// Unborrow the cell...
let old = std::mem::replace(self, BorrowedStuff::Unborrowed);
let (old_ref_mut, cell, mut gold) = match old {
BorrowedStuff::Borrowed { ref_mut, cell, gold } => (ref_mut, cell, gold),
_ => unreachable!()
};
drop(old_ref_mut);
let result = f(gold);
*self = cell.borrow_mut(&mut gold);
result
}
}
fn main() {
println!("Hello, world...");
let my_cell = StuffCell(RefCell::new(Stuff {}));
let mut my_gold = Gold { }; // Note: Gold ordinarily cannot be forged
let mut borrowed_stuff = my_cell.borrow_mut(&mut my_gold);
println!("Unborrowing stuff now...");
borrowed_stuff.unborrow_then(|_gold| {
println!("The stuff is unborrowed... wheeeeee...");
});
println!("The stuff is borrowed again.");
}
Errors:
Compiling playground v0.0.1 (/playground)
error[E0499]: cannot borrow `gold` as mutable more than once at a time
--> src/main.rs:61:33
|
46 | fn unborrow_then<'a: 'b, 'b, F, T>(&'a mut self, f: F) -> T
| -- lifetime `'b` defined here
...
59 | let result = f(gold);
| -------
| | |
| | first mutable borrow occurs here
| argument requires that `*gold` is borrowed for `'b`
60 |
61 | *self = cell.borrow_mut(&mut gold);
| ^^^^^^^^^ second mutable borrow occurs here
For more information about this error, try `rustc --explain E0499`.
error: could not compile `playground` (bin "playground") due to 1 previous error
I think I'm close, but an error occurs when I try to reborrow the cell after calling the closure.
It seems to think the gold is still held by the closure, but that isn't my intention. Neither the closure nor its result value should hold a reference to the gold after the closure has completed execution.
How do I enforce these rules within Rust and make this example compile?