Cached API function?

I am trying to create a struct that can represent a cached API function. In particular, callers can obtain the API's value using get method, which will only execute the underlying api_fn if the revalidation period has passed.

type APIFunction<B, T> = Box<dyn Fn(&B) -> T>;

pub struct CachedAPI<'a, B, T> {
    context: &'a B,
    value: T,
    api_fn: APIFunction<B, T>,
    last_updated: Instant,
    revalidate: Duration,
}

impl<'a, B, T> CachedAPI<'a, B, T> {
    pub fn new(api_fn: APIFunction<B, T>, context: &'a B, revalidate: Duration) -> Self {
        Self {
            context,
            value: Self::call(&api_fn, context),
            api_fn,
            last_updated: Instant::now(),
            revalidate,
        }
    }

    pub fn get(&mut self) -> &T {
        if Instant::now() - self.last_updated > self.revalidate {
          self.value = Self::call(&self.api_fn, self.context);
          self.last_updated = Instant::now();
        }

        &self.value
    }

    fn call(api_fn: &APIFunction<B, T>, context: &'a B) -> T {
        (api_fn)(context)
    }
}

This somewhat works. But where I am running into issues is trying to use this as a member of a struct. Consider,

  struct StructWithCachedAPI<'a> {
      x: CachedAPI<'a, Self, i32>,
  }

  impl StructWithCachedAPI {
    pub fn new() -> Self {
      Self {
          x: CachedAPI::new(Box::new(api_fn), &self, Duration::from_secs(1))
      }
    }

    pub fn compute_sync(&self) -> i32 {
      self.x + 1
    }
  }

Obviously, this doesn't work because self is not available when new is called. Does anyone have any ideas on how I can improve this abstraction s.t. I can achieve the desired interface.

Do you need context to be StructWithCachedAPI? Where does api_fn come in your example?

For caching/memoization you will need to use interior mutability (like Mutex) to keep the getter taking &self, because &mut self creates an exclusive borrow and will not allow obtaining more than one thing from the cache at a time.

https://doc.rust-lang.org/book/ch15-05-interior-mutability.html

And cached objects also typically need to be stored wrapped in Arc, so that you can return an item without it being tied to a temporary lifetime.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.