I have a struct that attempts to cache Sqlite query results and if they are None it will go and fetch the result fresh from the database.
The way I've implemented this is simply creating an impl with a method for each field, get_field(conn: &Conn), where Conn is a database connection.
My problem is readability, I want to just call get_field and it decides to either return the current cached field, or go and fetch (large blob) results and returns those:
impl MyStruct {
fn fetch(&self, table: &str, column: &str, row: Option<i32>) -> Vec<f32> {
... perform query to return blob from Sqlite database...
}
fn get_field_x(&self, conn: &Conn) -> Vec<f32> {
match &self.x { // not gonna work..
Some(x_ref) => x_ref // oh no, this is a reference...
None => fetch(..) // oh no, can't return reference to data created here...
}
}
}
The obvious work around is to just do a simple unwrap beforehand, which alleviates it from having to check if x is None:
let cached_or_live = my_struct.x.unwrap_or(my_struct.fetch(..., conn));
But now I have unwrap_or duplicated all over my code. Is there a way to still perform this check in an impl? I don't want to clone x_ref, as it can be large and that feels like a smelly work around.
This doesn't implement a cache / lazy initialization, though — if the field isn't set it fetches it on every call. Caching and returning a reference sounds like a use case for OnceCell.
Yes. All I did was the minimum required to solve the borrowing problem. The actual cache is up to OP, and I don't believe that's the part they were having issue with.
But once there is a cache, it will no longer make sense to conditionally return the value owned — it can and probably should be returned by reference (or, if needed for some reason, by cloned value). Thus Cow does not help when the intent is to fetch with caching.