(First time OnceLock
user)
In the below snippet, since the get_or_init
function of OnceLock
returns a &T
(thus &Result<T, E>
), but later usage of the value may need to bubble up the error value, I'd like to turn it into Result<&T, E>
.
But Error
is not Clone
due to reqwest::Error
and std::io::Error
are not Clone
. Thus cache_file.as_deref().map_err(|err| err.clone())
won't work. I know boxing those errors could make Clone
derivable on Error
.
I considered to change the return value of get_or_init
to &Option<Path>
(thus return value of cache_file
can be Option<&Path>
by calling as_ref
then I don't need to deal with the &Error
part. But then every later usage of the value would need a match
/if let
guard to get the path instead of propagating the error directly.
How to fix this? Or what's the best practice of this kind of static variable with Result
or Option
types?
Thanks.
use std::{
path::{Path, PathBuf},
sync::OnceLock,
};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("failed to fetch data")]
Fetch(#[source] reqwest::Error),
#[error("failed to access cache dir")]
CacheDir,
#[error("Other IO error")]
IO(#[from] std::io::Error),
}
static CACHE_FILE: OnceLock<Result<PathBuf, Error>> = OnceLock::new();
pub fn cache_file() -> Result<&'static Path, &'static Error> {
let cache_file = CACHE_FILE.get_or_init(|| {
dirs::cache_dir()
.map(|dir| dir.join("file.json"))
.ok_or(Error::CacheDir)
});
cache_file.as_deref()
}
// Maybe another option
// static CACHE_FILE: OnceLock<Option<PathBuf>> = OnceLock::new();
// pub fn cache_file() -> Option<&'static Path> {
// let cache_file = CACHE_FILE2.get_or_init(|| dirs::cache_dir().map(|dir| dir.join("file.json")));
//
// cache_file.as_deref()
// }
pub fn read_cache() -> Result<String, Error> {
// error: ^ the trait `From<&Error>` is not implemented for `Error`
let file = cache_file()?;
Ok(std::fs::read_to_string(file)?)
}
fn main() {}