Ownership issues with tokio and multiple types of futures

For the situation you are describing, Arc is probably the best choice unless you actually know it is a performance problem. Arc makes your futures Send, which allows you to use the very convenient boxed() method.

The slightly faster solution is to use Rc, but then instead of using boxed() you have to do the less ergonomic Box::new() around your future, and use Box<Future<Item = T, Error = E>> instead of BoxFuture<T, E>, also less ergonomic. However I don't think the upgrade is worth it unless you actually know the atomic counter is causing slowdown.

Arc or Rc are both shared ownership, and they're needed because the futures all need to have ownership of the data they're sharing - you don't know which future will complete last, so its not clear which one can 'own' the data and which one can just 'borrow' it.

The overhead of shared ownership is not usually a big deal as long as you only create the Rc or Arc once, at the time you create the event loop. Incrementing the reference count is pretty cheap. The expensive part is allocating the data in the heap, instead of on the stack (which happens when you call Rc::new or Arc::new), but once you've done that its not a big problem.

A final alternative, which avoids that heap allocation, is to have the data owned outside the event loop, and then pass references to that data into the futures that run on the event loop. This is much more difficult because those futures are no longer 'static, instead they have to have a lifetime threaded through them. You also need to make sure the data outlives the event loop, which can be tricky.

So each of these steps gets faster at the cost of ease of use. I really recommend using Arc and just making sure you don't create a new Arc for each new future, only clone it for each new future. That should probably be plenty fast. If not you can try advancing to faster architectures.

1 Like