Why Arc should not be an implementation detail

Here we analyze why not to use Arc, what the benefits of not using Arc are, how to make Arc opt-in, and how we came up with this whole thing.

It kinda all started when we made selfref, because, in C, there's this pattern: (this is C code obviously)

struct hexchat_server {
  stuff and_things;
  struct hexchat_context *contexts;
}
struct hexchat_context {
  struct hexchat_server *server;
  more stuff_and_things;
}

and selfref allows translating this pattern into Rust really easily. However you can't really turn these into Any's... or can you?

Let's think of a more practical example. Let's say you have an (FFI) library which uses lifetimes to keep track of objects, instead of, Arcs or Rcs:

fn put_thing_in_foo<'a, T>(foo: &'a Foo, bar: T) -> Handle<'a> { ... }

This is zero-cost, as there's no need to track allocations at runtime: the lifetimes guarantee that when Foo gets dropped, the Handles will no longer be accessible. (TODO find a real library which does this so we can use it as part of the tutorial...)

However, you do end up with lifetimes everywhere. How are you supposed to turn this into an dyn Any...?

Well, conveniently, self_cell and ouroboros actually enable converting these zero-cost lifetime-based libraries into Rc-based or Arc-based libraries as needed, instead of forcing every user to use Rc/Arc too. In other words, you only pay for what you need. And, while selfref is a library for making self-referential structs, it is not actually suitable for this, so you have to use self_cell or ouroboros. For example, with self_cell, all you need is this:

self_cell!(
    struct FooAndHandle {
        owner: Arc<Foo>,
        #[covariant]
        dependent: Handle,
    }
);

and then a FooAndHandle more or less acts like a Handle<'a> you can stick in a 'static! (TODO make this a real example, ideally a playground example, using the above real library.)

The minor drawback here is that self_cell and ouroboros aren't really optimized for this use-case, so you do pay for an extra allocation. Nevertheless, it's nice to have this flexibility, where someone who doesn't need Arc can really benefit from zero-cost lifetimes, and someone who does can get away from it.

If you're looking to just hide some lifetimes so you can shove them into a TypeMap or dyn Any or equivalent, self_cell and ouroboros are pretty good (even if not perfect) at solving this problem! As such, libraries should strongly prefer to use lifetimes instead of Arc/Rc where possible.