Question Details

No question body available.

Tags

rust dependency-injection ownership

Answers (1)

November 9, 2025 Score: 1 Rep: 560 Quality: Low Completeness: 80%

Your first approach is fine.

We can see an example of a similar trait in the standard library: std::os::fd::AsFd. By "similar", here I mean that all of AsFd's methods all take an immutable self parameter. Besides its non-generic implementors, AsFd is also implemented generically for a selection of reference types:

impl AsFd for &T { / / }
impl AsFd for &mut T { / / }
impl AsFd for Box { / / }
impl AsFd for Rc { / / }
impl AsFd for UniqueRc { / / }
impl AsFd for Arc { / / }

While I don't necessarily recommend that you do all of these for your traits (&mut T is not necessary because you can very easily turn &mut T into &T, UniqueRc is unstable, Box is generally covered by the owned implementation), generally speaking, &T, Rc, and Arc represent most, if not all, of the use cases that users of your traits will require. Then, when creating a cached blob store, people will be able to pass in owned caches, referenced caches, or Arc caches:

impl Cache for &C { / / }
impl Cache for Rc { / / }
impl Cache for Arc { / / }

fn createcachedblobstore(wrapped: BS, cache: C) -> impl BlobStore { CachingBlobStore { wrapped, cache } }

#[derive(Clone, Copy)] struct DummyBlobStore; #[derive(Clone, Copy)] struct DummyCache;

impl BlobStore for DummyBlobStore { /* */ }

impl Cache for DummyCache { /* */ }

let blobstore = DummyBlobStore; let cache = DummyCache;

let = createcachedblobstore(blobstore, cache); // owned let = createcachedblobstore(blobstore, &cache); // referenced let = createcachedblobstore(blobstore, Arc::new(cache)); // arc'd

Playground

An alternate approach could automatically deal with all the different reference types, by leveraging the Borrow trait:

fn createcachedblobstoreref(
    wrapped: impl BlobStore,
    cache: impl Borrow,
) -> impl BlobStore {
    struct CacheRef(CR, PhantomData);
    impl Cache for CacheRef
    where
        C: Cache + ?Sized,
        CR: Borrow,
    {
        fn get(&self, key: &str) -> Option {
            C::get(self.0.borrow(), key)
        }
        fn set(&self, key: &str, value: Vec) {
            C::set(self.0.borrow(), key, value)
        }
    }

let cache = CacheRef(cache, PhantomData); CachingBlobStore { wrapped, cache } }

The downside of this approach is that call sites will have to manually specify exact what type to use as the cache, because Rust can no longer infer what trait implementation of Cache to use (theoretically, one could impl Borrow for CR and impl Borrow for CR):

let blobstore = DummyBlobStore;
let cache = DummyCache;

let = createcachedblobstoreref::(blobstore, cache); // owned let = createcachedblobstoreref::(blobstore, &cache); // referenced let = createcachedblobstoreref::(blobstore, Arc::new(cache)); // arc'd

Playground

Theoretically, this is more flexible, able to take any type that implements Borrow, for a given C: Cache, which would include most, if not all, smart pointer types.

You could also use AsRef instead of Borrow, which has less strict requirements, but this means that the createcachedblobstoreref function would not be able to take owned caches at all, leading to a similar problem as your second and third solutions.