When to Use Arc Instead of Vec

Rust lets you do efficient reference-counted strings and dynamic arrays using Arc basically just as easily as their owning (and deep-cloning) equivalents, String and Vec respectively. So why not use them as a reasonable default, until you actually need the mutability that String and Vec provide? Get into the weeds with me here, feat. some cool visualizations, with special guest appearance from Box.

This video assumes some familiarity with Rust and its core smart pointer types, namely Vec/String/Rc/Arc/Box, along with data structures like HashMap and BTreeMap, and traits like Clone, Hash, Ord, and serde::{Serialize, Deserialize}.

serde feature flag for Rc/Arc
Arc docs
Vec docs
Smart pointers in Rust • Crust of Rust
animations

lavafroth,

If the data represented in the Arc is supposed to be immutable, why not pass around a reference of Vec<T> instead of cloning the Arc<[T]> over and over? The latter appears more expensive. Arc<String> is always going to be worse than Arc<str> because Arc<String> is roughly equivalent to Arc<Vec<char>>. Lastly, instead of the overhead of a tuple struct, we could define

pub type MonsterId = Arc<str>;

and maybe define and implement traits on it for additional methods.

Dumhuvud,

Arc<String> is roughly equivalent to Arc<Vec<char>>.

Nitpick: it's Vec<u8>, not Vec<char>.

lavafroth,

Gee thanks, nice catch!

nous,

I think the video misses a lot of points. Most of the time you just want one owned String and then pass around a lot of &str references. For the use case they describe where lifetimes make this hard, I don't think the solution is to use Arc at all, but instead use u32 or similar for your id instead of a string id. That is way cheaper to copy around.

There are times to use a Box or Arc str instead, but those are not as common as just string references or u32 ids.

ericjmorey,
@ericjmorey@programming.dev avatar

I'm confused by your response. What are you saying is cheaper to copy compared to what?

nous,

It is cheaper to copy a Id(u32) then a Id(Arc<str>) The Arc requires a lock, incrementing a counter and copying a pointer. The Id is only a copy of a u32. For the use case the video describes it makes more sense to use simple IDs and not stringy typed ids for passing around everywhere to avoid this extra cost (as well as the cost of comparing strings when you need to fetch the data the id is meant to represent).

ericjmorey,
@ericjmorey@programming.dev avatar

Thank you for adding this detail

Vorpal, (edited )

I believe the video is somewhat incorrect: Arc (and Rc) need a reference count, which you missed in your description of the overhead. (EDIT: You mentioned this later in the video. )

The downside of all of Arc or Rc is of course (as you pointed out towards the end of the video) that you need to do all that reference counting and store the reference count (as I mentioned above). A box of a slice would indeed be cheaper. But if the data is static (or will at least live for the rest of the program), you might consider just leaking it (Box::leak) to get a static lifetime reference. That can then be cheaply shared.

ericjmorey,
@ericjmorey@programming.dev avatar

I want to be clear that I didn't make this video. I just thought it was worthy of sharing as someone who doesn't have a good handle on Rust or best practices in using Rust.

  • All
  • Subscribed
  • Moderated
  • Favorites
  • rust@programming.dev
  • ethstaker
  • DreamBathrooms
  • everett
  • magazineikmin
  • Durango
  • InstantRegret
  • rosin
  • Youngstown
  • love
  • slotface
  • GTA5RPClips
  • kavyap
  • mdbf
  • ngwrru68w68
  • megavids
  • thenastyranch
  • tacticalgear
  • cubers
  • modclub
  • osvaldo12
  • cisconetworking
  • tester
  • khanakhh
  • normalnudes
  • provamag3
  • anitta
  • Leos
  • JUstTest
  • All magazines