I have the following code in Rust:
pub struct RegExpFilter {
...
regexp_data: RefCell<Option<RegexpData>>,
...
}
struct RegexpData {
regexp: regex::Regex,
string: String
}
...
pub fn is_regexp_compiled(&self) -> bool {
self.regexp_data.borrow().is_some()
}
pub fn compile_regexp(&self) -> RegexpData {
...
}
fn regexp(&self) -> ®ex::Regex {
if !self.is_regexp_compiled() { // lazy computation that mutates the struct
self.regexp_data.replace(Some(self.compile_regexp()));
}
&self.regexp_data.borrow().as_ref().unwrap().regexp
}
pub fn matches(&self, location: &str) -> bool {
self.regexp().find(location)
}
regexp is calculated lazily, capturing &mut self
i undesired so RefCell
is used.
I'm getting the following message:
&self.regexp_data.borrow().as_ref().unwrap().regexp
| ^-------------------------^^^^^^^^^^^^^^^^^^^^^^^^^
| ||
| |temporary value created here
| returns a value referencing data owned by the current function
The compiler message seems to be clear: Ref
is temporarily created by borrow()
and returned outside. However i believe Option
(self.regexp_data
) is owned by RefCell
which is owned by the struct itself, so it should be fine to use it internally (since the function is not pub
).
I've also tried the following (and it fails with the same message)
fn regexp(&self) -> impl Deref<Target = regex::Regex> + '_ {
if !self.is_regexp_compiled() {
self.regexp_data.replace(Some(self.compile_regexp()));
}
Ref::map(self.regexp_data.borrow(), |it| &it.unwrap().regexp)
}
How can i solve it?
You can fix the Ref::map
version by using .as_ref()
to convert the &Option<_>
to a Option<&_>
in order to to unwrap as a reference:
fn regexp(&self) -> impl Deref<Target = regex::Regex> + '_ {
if !self.is_regexp_compiled() {
self.regexp_data.replace(Some(self.compile_regexp()));
}
Ref::map(self.regexp_data.borrow(), |it| &it.as_ref().unwrap().regexp)
// ^^^^^^^^
}
In this scenario, I'd advocate for using OnceCell
from the once_cell crate:
use once_cell::sync::OnceCell;
pub struct RegexpData {
regexp: regex::Regex,
string: String,
}
pub struct RegExpFilter {
regexp_data: OnceCell<RegexpData>,
}
impl RegExpFilter {
pub fn compile_regexp(&self) -> RegexpData {
unimplemented!()
}
fn regexp(&self) -> ®ex::Regex {
&self.regexp_data.get_or_init(|| self.compile_regexp()).regexp
}
}
You can simply use get_or_init
to get the same effect. OnceCell
and Lazy
(in the same crate) are very convenient for lazy-evaluation.
See it on the playground.