I'm trying to write a function that returns some sort of list. Since this function is pure, and I'm likely to call it multiple times, I want to cache the results. To avoid allocations, I want to return references that all point to the same memory location, so that I only need to perform an allocation once per unique call of the function.
In principle, I could store each result as a vector in a cache, and return references to the result vector's slice. This looks unsafe, but (I think) isn't. If you never delete or modify elements of the cache, then your slice references are safe. The vectors might be moved when you add to the cache, but their slices shouldn't be.
Obviously the borrow checker won't accept this solution. As far as it's concerned, the cache is borrowed when your function returns, and you can't call it again because that requires a mutable borrow.
The best solution I have so far is on the playground here, and included below:
use std::collections::HashMap;
use std::rc::Rc;
fn n_trues(n: usize, cache: &mut HashMap<usize, Rc<[bool]>>) -> Rc<[bool]> {
cache
.entry(n)
.or_insert_with(|| vec![true; n].into())
.clone()
}
fn main() {
let mut cache = HashMap::new();
let zero = n_trues(0, &mut cache);
let one = n_trues(1, &mut cache);
let other_zero = n_trues(0, &mut cache);
assert!(Rc::ptr_eq(&zero, &other_zero));
for x in [zero, one, other_zero].iter() {
println!("{:?}", x);
}
}
The Rc
satisfies the borrow checker: deleting from the cache won't invalidate the other references, and you can't mutate the contents of an Rc
. However, the actual refcounting is superfluous: we ensure that the cache goes out of scope only at the end of the program, so the cached values are not freed until then. This is the sort of thing the borrow checker can check: it can ensure that cache outlives all the results.
Is there any way to eliminate the refcount without resorting to unsafe code?