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, Arc
s or Rc
s:
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 Handle
s 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.