I'm having trouble working with references in a trait where one implementer uses lazy_static and the other uses RefCell. Is there any way to get rid of this compile error?
use {
std::{
cell::{
Ref,
RefCell,
},
convert::Infallible as Never,
fs::File,
io,
ops::Deref,
path::{
Path,
PathBuf,
},
},
lazy_static::lazy_static,
serde_derive::Deserialize,
};
#[derive(Deserialize)]
struct Region {
region_name: String,
}
trait Rando {
type Err;
// Uses Box<dyn Deref> since the RandoStatic impl returns a reference while RandoDynamic returns a std::cell::Ref
fn regions<'a>(&'a self) -> Result<Box<dyn Deref<Target = Vec<Region>> + 'a>, Self::Err>;
}
struct RandoStatic;
lazy_static! {
static ref REGIONS: Vec<Region> = vec![
Region { region_name: format!("Kokiri Forest") },
Region { region_name: format!("KF Outside Deku Tree") },
Region { region_name: format!("KF Links House") },
];
}
// In the actual code, this impl and the lazy statics above are generated by a proc macro.
impl Rando for RandoStatic {
type Err = Never;
fn regions<'a>(&'a self) -> Result<Box<dyn Deref<Target = Vec<Region>> + 'a>, Never> {
Ok(Box::new(&*REGIONS))
}
}
struct RandoDynamic {
path: PathBuf,
regions: RefCell<Option<Vec<Region>>>,
}
impl RandoDynamic {
fn new(path: impl AsRef<Path>) -> RandoDynamic {
RandoDynamic {
path: path.as_ref().to_owned(),
regions: RefCell::default(),
}
}
}
#[derive(Debug)]
enum RandoError {
Io(io::Error),
Json(serde_json::Error),
}
// The data on disk is expected not to change, so this impl uses RefCells as caches.
impl Rando for RandoDynamic {
type Err = RandoError;
fn regions<'a>(&'a self) -> Result<Box<dyn Deref<Target = Vec<Region>> + 'a>, RandoError> {
if self.regions.borrow().is_none() {
let world_path = self.path.join("data").join("World").join("Overworld.json");
let regions = serde_json::from_reader(File::open(world_path).map_err(RandoError::Io)?).map_err(RandoError::Json)?;
*self.regions.borrow_mut() = Some(regions);
}
Ok(Box::new(Ref::map(self.regions.borrow(), |regions| regions.as_ref().expect("just inserted"))))
}
}
enum RegionLookupError<R: Rando> {
MultipleFound,
NotFound,
Rando(R::Err),
}
impl Region {
fn by_name<'a, R: Rando>(rando: &'a R, name: &str) -> Result<&'a Region, RegionLookupError<R>> {
let all_regions = rando.regions().map_err(RegionLookupError::Rando)?;
let mut candidates = all_regions.iter().filter(|region| region.region_name == name);
match (candidates.next(), candidates.next()) {
(None, _) => Err(RegionLookupError::NotFound),
(Some(region), None) => Ok(region),
(Some(_), Some(_)) => Err(RegionLookupError::MultipleFound),
}
}
}
Errors:
Compiling playground v0.0.1 (/playground)
error[E0515]: cannot return value referencing local data `*all_regions`
--> src/lib.rs:98:37
|
95 | let mut candidates = all_regions.iter().filter(|region| region.region_name == name);
| ----------- `*all_regions` is borrowed here
...
98 | (Some(region), None) => Ok(region),
| ^^^^^^^^^^ returns a value referencing data owned by the current function
error: aborting due to previous error
For more information about this error, try `rustc --explain E0515`.
error: could not compile `playground`
To learn more, run the command again with --verbose.