Trying to un-XY this problem as much as possible
I have a struct like this
struct Thing {
text:String
other : Other
}
and a method signature like this
impl Thing {
fn get_text(&self) -> &str{
}
}
Note that get_text is immutable. ( I cant make it mutable. I am am dropping a replacement implementation of a componenet into a large project, I cannot change the signatures)
Other
has a method get_text
too. It returns &[String]
. I need to convert that slice into a '\n' delimited String
and return a &str
pointing at it.
This works in a mutable environment;
fn get_text(&mut self) -> {
self.msg = self.other.get_text().join("\n");
&self.msg
}
But it has to be immutable.
So I tried to use RefCell.
struct Thing {
text:RefCell<String>,
other : Other
}
pub fn get_text(&self) -> Ref<String> {
let text = self.other.get_text().join("/n");
self.text.replace(text);
self.text.borrow()
}
I can almost get it to work. Depending on how the caller uses that return value, sometimes the DeRef trait kicks in and sometimes not. But I also get complicated borrow errors in the calling code eg (this is caller's code, self.input
is an instance of Thing
)
fn foo(&mut self) {
let msg = self.input.get_text();
let signed_msg = self.bar(&msg);
if let std::result::Result::Ok(signed_msg) = signed_msg {
self.input.set_text(signed_msg);
}
}
produces this
error[E0502]: cannot borrow `self.input` as mutable because it is also borrowed as immutable
--> src\components\commit.rs:352:4
|
349 | let msg = self.input.get_text();
| ---------- immutable borrow occurs here
...
352 | self.input.set_text(signed_msg);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
353 | }
354 | }
| - immutable borrow might be used here, when `msg` is dropped and runs the destructor for type `Ref<'_, std::string::String>`
note that this
let signed_msg = self.bar(&msg);
^
is an edit I already did just to see what I can get working. I really would prefer not to make any changes to the calling code
I feel that I should be able to return a &str somehow, rather than a Ref. The lifetime of self.msg is guaranteed to be OK, I mean in a mutable env I could simply return &self.msg. My caller is not expecting anything mutable to be returned, they are just reading, but I dont see a RefCell method that returns a &T. Maybe I should use something else.
EDIT
I found that I can make this work
struct Thing {
text:OnceCell<String>,
other : Other
}
pub fn get_text(&self) -> &str {
let text = self.other.get_text().join("/n");
self.msg.get_or_init(|| text).as_str()
}
But That only works if get_text is only called once (which I cannot be sure of). Feel I am getting very close
EDIT 2
Since I own all the code paths that lead to other
updating its internal state I can call msg.take
in those paths (they are all &mut self functions). So any update erases the msg:OnceCell
and the next call to get_text will rebuild it. Perfect