jaemk / cached

Rust cache structures and easy function memoization
MIT License
1.57k stars 95 forks source link

Cached proc macro doesn't keep where clause #217

Open gerald-pinder-omnicell opened 3 months ago

gerald-pinder-omnicell commented 3 months ago

Ran into an issue on a function that has a where clause on it. For some reason the where clause doesn't get copied over to the no_cache function but the primed_cache function and the replaced function has the where clause. I'm on version v0.53.1 using the features tokio and async.

Here's the function:

#[cached(
    result = true,
    sync_writes = true,
    key = "String",
    convert = r#"{ format!("{}:{}", path.as_ref(), path_key.as_ref()) }"#
)]
async fn get_secret_internal<T, U>(
    client: &VaultClient,
    path: T,
    path_key: U,
) -> Result<Zeroizing<String>>
where
    T: AsRef<str> + Send + Sync,
    U: AsRef<str> + Send + Sync,
{
    let path = path.as_ref();
    let path_key = path_key.as_ref();

    // ...
}

Here's the error:

error[E0599]: no method named `as_ref` found for type parameter `T` in the current scope
  --> src/vault.rs:84:21
   |
75 | async fn get_secret_internal<T, U>(
   |                              - method `as_ref` not found for this type parameter
...
84 |     let path = path.as_ref();
   |                     ^^^^^^ method not found in `T`
   |
   = help: items from traits can only be used if the type parameter is bounded by the trait
help: the following trait defines an item `as_ref`, perhaps you need to restrict type parameter `T` with it:
   |
75 | async fn get_secret_internal<T: AsRef, U>(
   |                               +++++++

error[E0599]: no method named `as_ref` found for type parameter `U` in the current scope
  --> src/vault.rs:85:29
   |
75 | async fn get_secret_internal<T, U>(
   |                                 - method `as_ref` not found for this type parameter
...
85 |     let path_key = path_key.as_ref();
   |                             ^^^^^^ method not found in `U`
   |
   = help: items from traits can only be used if the type parameter is bounded by the trait
help: the following trait defines an item `as_ref`, perhaps you need to restrict type parameter `U` with it:
   |
75 | async fn get_secret_internal<T, U: AsRef>(
   |                                  +++++++

For more information about this error, try `rustc --explain E0599`.

Here's the expanded rust using cargo expand:

    ///Cached static for the [`get_secret_internal`] function.
    static GET_SECRET_INTERNAL: ::cached::once_cell::sync::Lazy<
        ::cached::async_sync::Mutex<cached::UnboundCache<String, Zeroizing<String>>>,
    > = ::cached::once_cell::sync::Lazy::new(|| ::cached::async_sync::Mutex::new(
        cached::UnboundCache::new(),
    ));
    ///Origin of the cached function [`get_secret_internal`].
    async fn get_secret_internal_no_cache<T, U>(
        client: &VaultClient,
        path: T,
        path_key: U,
    ) -> Result<Zeroizing<String>> {
        let path = path.as_ref();
        let path_key = path_key.as_ref();

       // ...
    }
    ///This is a cached function that uses the [`GET_SECRET_INTERNAL`] cached static.
    async fn get_secret_internal<T, U>(
        client: &VaultClient,
        path: T,
        path_key: U,
    ) -> Result<Zeroizing<String>>
    where
        T: AsRef<str> + Send + Sync,
        U: AsRef<str> + Send + Sync,
    {
        use cached::Cached;
        use cached::CloneCached;
        let key = {
            {
                let res = ::alloc::fmt::format(
                    format_args!("{0}:{1}", path.as_ref(), path_key.as_ref()),
                );
                res
            }
        };
        let mut cache = GET_SECRET_INTERNAL.lock().await;
        if let Some(result) = cache.cache_get(&key) {
            return Ok(result.to_owned());
        }
        let result = get_secret_internal_no_cache(client, path, path_key).await;
        if let Ok(result) = &result {
            cache.cache_set(key, result.clone());
        }
        result
    }
    ///Primes the cached function [`get_secret_internal`].
    #[allow(dead_code)]
    ///This is a cached function that uses the [`GET_SECRET_INTERNAL`] cached static.
    async fn get_secret_internal_prime_cache<T, U>(
        client: &VaultClient,
        path: T,
        path_key: U,
    ) -> Result<Zeroizing<String>>
    where
        T: AsRef<str> + Send + Sync,
        U: AsRef<str> + Send + Sync,
    {
        use cached::Cached;
        let key = {
            {
                let res = ::alloc::fmt::format(
                    format_args!("{0}:{1}", path.as_ref(), path_key.as_ref()),
                );
                res
            }
        };
        let mut cache = GET_SECRET_INTERNAL.lock().await;
        let result = get_secret_internal_no_cache(client, path, path_key).await;
        if let Ok(result) = &result {
            cache.cache_set(key, result.clone());
        }
        result
    }
gerald-pinder-omnicell commented 3 months ago

I can currently work around this limitation by putting trait bounds in the function generic type section (<T, U>).

omid commented 1 month ago

@gerald-pinder-omnicell I forked this project and where works there: https://crates.io/crates/kash