I need to maintain two lists that contain essentially the same data, and need to be kept in sync. However, the synchronisation in my real application is slow and uncommon enough that it needs to happen lazily, and I'm struggling with the mutability issue.
I've made a minimal example here of what I mean, which also serves to point to a few names in my question: Playground
use std::cell::RefCell;
struct LengthList {
pub kms: Vec<f32>,
pub miles: RefCell<Vec<f32>>,
miles_in_sync: bool,
}
impl LengthList {
pub fn add_km(&mut self, km: f32) {
self.kms.push(km);
self.miles_in_sync = false;
}
pub fn get_km(&self, index: usize) -> f32 {
self.kms[index]
}
pub fn get_mile(&self, index: usize) -> f32 {
if !self.miles_in_sync {
self.sync();
}
self.miles.borrow()[index]
}
fn sync(&self) {
//In reality, this "sync" would be something really heavy.
self.miles.borrow_mut().clear();
for km in self.kms.iter() {
self.miles.borrow_mut().push(km / 1.6);
}
}
// pub fn iter_miles(&self) -> core::slice::Iter<f32> {
// self.miles.borrow().iter() //Causes compilation error!
// }
}
fn main() {
let mut ll = LengthList {
kms: vec!(),
miles: RefCell::new(vec!()),
miles_in_sync: true,
};
ll.add_km(3.14);
println!("The distance in km: {}", ll.get_km(0));
println!("The distance in miles: {}", ll.get_mile(0));
}
In this snippet, you can add to the kms vec through the add_km function. When you then try to request that length in miles though get_mile(0), it will automatically synchronise the two lists and then return the length converted to miles.
Because to the user it would be illogical that the get_mile function would require a &mut self reference, I'm using interior mutability here with a RefCell. In truth, this function does modify the struct, because it may trigger a sync. But the RefCell hides that.
However I'd also like the user to be able to iterate over the synchronised lists. And this is where the problem arises. The simplest attempt written in iter_miles in my example doesn't work because it's returning a reference to a temporary: The borrow() call returns a temporary Ref object that doesn't survive the end of the iter_miles function call, but the return value needs to survive that.
Isn't it possible to iterate over the contents of a RefCell? And if not, is there another construction I could use to keep these two lists lazily in sync without adding mut to every function?
I tried to simplify the example as much as I could. In reality, both lists can be changed and are lazily kept in sync with each other. The reason that it's slow to synchronise is that one list is a simple Vec in RAM, but the other is an ArrayFire array in the GPU's VRAM.