What is the best way to cache method return value in struct field?

I have a method f that returns a Vec<usize> originally. Since f is expensive, is there a way to cache the result in a struct field, e.g. cached without &mut self? The equivalent Python code is

def f(self):
    if self.cache is None:
        self.cache = compute()
    return self.cache

What if the function is called from multiple threads? Is it ok to clone the entire vector on second call?

There are no multithreads and the vector is large so we should avoid cloning the vector.

I'd probably put it behind some sort of Cache wrapper which stashes a copy of the arguments and return value in a HashMap and checks the HashMap before calling the function.

There are various ways you can change the implementation (using reference-counted pointers like Arc<[T]> instead of cloning the result/arguments, internal mutability/synchronisation, etc.), but here's a Playground snippet with the general idea:

(playground)

Yes, this is done with Interior mutability - The Rust Reference

Wrap your cache in Mutex or RefCell type, and you'll be able to modify it via & shared reference.

1 Like

Or you can wrap the field with the Lazy type to lazily initialize it.

While you can do this as per the other replies, I’d consider avoiding the hiding of side effects. Side effects make the code hard to reason about.

1 Like

You could use something like this maybe:

use once_cell::unsync::OnceCell;
use std::rc::Rc;

fn compute() -> Vec<usize> {
    vec![42]
}

struct Cache {
    cache: OnceCell<Rc<[usize]>>,
}
impl Cache {
    fn f(&self) -> Rc<[usize]> {
        Rc::clone(self.cache.get_or_init(|| compute().into()))
    }
}
Or—if you don’t need an owned “vector” returned from `f`—, you could return a reference, e.g. like this (click to expand)
use once_cell::unsync::OnceCell;

fn compute() -> Vec<usize> {
    vec![42]
}

struct Cache {
    cache: OnceCell<Vec<usize>>,
}
impl Cache {
    fn f(&self) -> &[usize] {
        self.cache.get_or_init(|| compute())
    }
}