Is my generalised reference crate a reasonable idea?


#1

Background

tl;dr This started out as a resource-references-resource solution and evolved into a more general Cow.

One of the libraries I maintain defines a hierarchy of resources; e.g., a top-level Environment type, and a Database<'a> type that notionally contains an &'a Environment, etc. This works great until you want to ship the two around together in a struct such as

struct Resources {
  env: Environment,
  db: Database<'?>, // Needs to reference `env`
}

Since this isn’t legal, one currently has to resort to using unsafe code (by hand, or like owning_ref) or contorting the program to hold everything on the stack.

But I also didn’t want to force Arc on everyone since it makes it harder to statically reason about the actual lifetimes of values.

So I essentially started with something like

enum Reflike<'a, T> {
  Owned(T),
  Borrowed(&'a T),
  Shared(Arc<T>),
}

and then extended it to be generic over shared reference types (eg, Arc vs Rc), to handle Borrow similarly to Cow and eventually ended up with this very general thing below.

The crate in question

Docs Repo

Essentially, this gives you a type Supercow<'a, T> that is mostly usable as &'a T and has similar features as Cow<'a,T>, plus the ability to work with Arc and friends, and Into conversions from bare owned/borrowed/shared values. By default it is only a pointer wider than a normal reference.

In the case of the library above, the struct issue is solved by the client code choosing to use Arcs instead, resulting in, e.g.,

struct Resources {
  env: Arc<Enviornment>,
  db: Database<'static>, // Holds its own `Arc<Environment>` in a `Supercow`
}

while reference-based client code can continue using references.

Basically I’m hoping for feedback on a couple points before actually putting this on crates.io:

  • Does this seem generally useful? I was kind of surprised to not find anything similar already existing, but maybe I didn’t look for the right thing.

  • Is it worth it? The code does a lot of… interesting things with pointers in the name of keeping the struct small and having a fast Deref. It also has a number of somewhat esoteric generic parameters.


#2

To your first question, I think this is absolutely worth publishing, and I’m saying this as the author of a crate that also addresses this type of problem but in a completely different way. Having more options for advanced reference/lifetime handling is something rust could surely use, and this looks like a fantastic addition.

This kind of scenario arises with regularity when designing complex APIs in rust, so it’s absolutely worth it to explore options for handling it. Every use case will be slightly different, and could benefit from having different options to choose from for the most ergonomic fit.

I’ve only given your docs a quick read so far, so I can’t comment on the finer nuances, but it seems very thorough and well considered.


#3

Thanks for the feedback! I also got a positive response from some friends, so it’s now on crates.io.