Here's some example code for a simple struct World
which contains a vector of Object
s, for which each Object
is assigned a category.
#[derive(PartialEq, Debug)]
enum Category {
A, B, C, D
}
#[derive(Debug)]
struct Object {
pub someData: f64,
pub category: Category
//...
}
struct World {
pub objects: Vec<Object>,
//...
}
impl World {
pub fn new(objects: Vec<Object>) -> Self {
World { objects }
}
pub fn getObjectsOfCategoryA(&self) -> Vec<&Object> {
self.objects.iter().filter(|x| x.category == Category::A).collect()
}
}
The World
also offers the user the ability to query the objects of category A in particular.
But, what if I want to call getObjectsOfCategoryA()
frequently enough that, for performance reasons, I want to cache the result of the function? Ideally this caching should be opaque to any caller of getObjectsOfCategoryA()
.
Let's add the restriction that objects
is guaranteed not to be mutated after the World
is created.
(I don't know how to express this restriction to Rust, but we'll get back to that later).
Object
doesn't derive Copy
or Clone
so we can't just create a new vector of cloned objects as our cached vector.
One way to do it would be to use Arc
:
struct World {
objects: Vec<Arc<Object>>,
objectsOfCategoryA: Vec<Arc<Object>>
}
impl World {
pub fn new(objects: Vec<Object>) -> Self {
let arcObjects: Vec<Arc<Object>> = objects.into_iter()
.map(|x| Arc::new(x)).collect();
let objectsOfCategoryA = arcObjects.iter().filter(|x| x.category == Category::A)
.map(|x| x.clone()).collect();
World { objects: arcObjects, objectsOfCategoryA }
}
pub fn getObjectsOfCategoryA(&self) -> &Vec<Arc<Object>> {
&self.objectsOfCategoryA
}
}
This strikes me as less than ideal because:
- We need to change the storage pattern of the main
objects
vector - This doesn't intuitively indicate to the reader of the code that
objectsOfCategoryA
is simply a view intoobjects
- If
objects
is accidentally mutated, this will silently fail. Ideally, I'd like a compile error if anything tries to mutateobjects
afterWorld
has been constructed.
If there was some way for objectsOfCategoryA
to be a Vec<&Object>
that would feel 'right' to me, but from research I've done it seems like that's not possible.
I'm new to Rust, so it's quite possible I'm looking at this from too much of an OOP perspective. Can anyone indicate an idiomatic way to achieve this kind of caching?