Here is my struct and I need a get_mut
function that returns a mutable reference to a value owned either by
ctx.scope
or ctx.parent.scope
recursively
pub type RuntimeContext<'c> = Rc<RefCell<Context<'c>>>;
pub struct Context<'c> {
pub parent: Option<RuntimeContext<'c>>,
pub scope: HashMap<String, Value>,
pub eval: Value,
}
This is what I tried, but as expected the borrow checker complains:
error[E0515]: cannot return reference to temporary value
--> src/lib.rs:20:13
|
20 | parent.borrow_mut().get_mut(key) // here it breaks
| -------------------^^^^^^^^^^^^^
| |
| returns a reference to data owned by the current function
| temporary value created here
What other way is there to achieve this?
(the parent ctx is guaranteed to outline the current ctx)
impl<'c> Context<'c> {
pub fn get_mut(&mut self, key: &str) -> Option<&mut Value> {
if let Some(v) = self.scope.get_mut(key) {
Some(v)
} else if let Some(parent) = &mut self.parent {
parent.borrow_mut().get_mut(key) // here it breaks
} else {
None
}
}
// ... some other methods
}
You can't; there simply is no way to return a &mut
reference to member of an object wrapped in Rc<RefCel<>>
. RefCell
requires a guard object to keep track whether the object is currently borrowed. A &mut
reference, however, does no such thing, making it impossible to know for the RefCell
that the &mut
reference exists after its guard got dropped. Which happens at the end of the function.
It would, in general, be possible to return something that still contains the guard object, but that would of course not be compatible with the other return path where you want to return a reference from the scope
member.
There are two ways I could think of how to achieve this:
The enum return value is quite difficult to get right and will become very complicated due to the multiple-layers-deep nesting of variables.
EDIT: I don't think the enum idea would work because it would run into lifetime problems.
So I personally would go with the second option.
Here is how that could look like:
use std::{cell::RefCell, collections::HashMap, rc::Rc};
#[derive(Debug)]
pub struct Value;
pub type RuntimeContext<'c> = Rc<RefCell<Context<'c>>>;
pub struct Context<'c> {
pub parent: Option<RuntimeContext<'c>>,
pub scope: HashMap<String, Value>,
pub eval: Value,
}
impl<'c> Context<'c> {
pub fn get_mut<R>(&mut self, key: &str, f: impl FnOnce(Option<&Value>) -> R) -> R {
if let Some(v) = self.scope.get_mut(key) {
f(Some(v))
} else if let Some(parent) = &mut self.parent {
parent.borrow_mut().get_mut(key, f) // here it breaks
} else {
f(None)
}
}
// ... some other methods
}
// Example on how to use this
pub fn do_something(context: &mut Context) {
let y = context.get_mut("foobarkey", |value| {
println!("Got value: {:?}", value);
let computed_result = 42;
computed_result
});
println!("Result: {}", y);
}