3 ms·
Could someone help explain the difference between Rust ARC (Atomically Reference Counted) vs. Switch ARC (Automatic Reference Counting)? Do the language design
by low_tech_punk 4y ago
Could someone help explain the difference between Rust ARC (Atomically Reference Counted) vs. Switch ARC (Automatic Reference Counting)?
Do the language designers intentionally chosen the same acronym to confuse us?
- oconnor663 4y agoRust's Arc is very similar to C++'s shared_ptr. It's a generic container type, which does reference counting on the inside. I haven't written Swift, but I believe reference counting is mostly automatic and transparent there, more like a GC strategy than like a container. Not sure whether anyone's to blame for the name collision :)
- zaphar 4y agoWith one very important difference. Unless you use a thread_safe interior mutability implementation like Mutex Arc won't allow you modify or change the wrapped value. C++'s shared_ptr does not enforce the same. All of which means that while the shared_ptr control block is thread safe what the shared_ptr contains may or may not be. This has caused, and will continue to cause, no end of confusion for folks doing C++ especially for less experienced devs.
- oconnor663 4y agoThere are many differences when it comes to safety: - Like you said, Rust doesn't let you data race on the contents of an Arc. - Rust also doesn't let you data race on the Arc itself (i.e. repointing a global Arc without synchronization). This is Bug #5 in this great talk: https://youtu.be/lkgszkPnV8g https://youtu.be/lkgszkPnV8g - Rust also doesn't let you retain a reference to the contents of an Arc longer than you retain the Arc. This is the bug that C++ Core Guideline R.37 is about: http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rr-smartptrget http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#... But I'm not sure I'd call these Arc vs shared_ptr differences per se, because almost any comparison between a Rust container and its C++ equivalent looks similar.
- kannanvijayan 4y agoThe designers and implementors are just using standard terminology in the field. In both cases where the concept applies, the underlying objects are being reference counted, with counts being atomically incremented/decremented to allow for thread safety. Swift builds the behaviour into the language runtime for objects where the behaviour applies, while Rust eschews language runtimes and provides it as an explicit wrapper structure in its standard library.
- bobbylarrybobby 4y agoThe different is that Rust’s isn't automatic. Swift does ARC on all heap-allocated types except where it can prove it doesn't have to.
- Jtsummers 4y agoIt's automatic for Swift because Objective-C (where ARC was introduced) already had reference counting but you had to manually indicate whether an object should be retained or released at any point. ARC added in the retain/release and such automatically during compilation.
- Gankra 4y agoThey are exactly the same except for when they're not. (On 64-bit) Rust very naively has two 64-bit integers for the strong and weak count, Swift packs them into only one. Swift also packs in several extra flags for various things [0]. These flags mean that retain/release (increment/decrement) is actually an atomic compare-and-swap instead of a fetch-add. Allegedly performance issues with this were fixed by the hardware team, just, optimizing CASes better. Swift also has to interop with ObjC "weak" pointers which have move constructors because their address is registered with a global map which is used to null them out when all strong counts go away, but I don't think this changes the design much when not using them. Swift ARC is built into the language and a huge amount of the compiler's energy is dedicated to optimizing it. This is why it's part of the calling convention (+1/+0), why there are special getter/setter modes with different ARC semantics, why many stdlib functions are annotated with "this has such-and-such semantics" and so on. Swift ARC is also very pervasive, as basic collections are all ARC-based CoW, all classes are ARC, and I think existentials and implicit boxes also go through ARC for uniformity? You can in principle avoid ARC completely by restricting yourself to value types (structs/primitives) but this is complicated by polymorphic generics and resilient compilation necessitating some dynamic allocations. ARC is also why historically Swift gave itself fairly extreme leniency on running destructors "early" based on actual use [1]. Eliminating a useless +1 can be the difference between O(n) and O(n^2) once CoW gets involved! By contrast in Rust it's "just" a library type which you have to clone/drop (increment/decrement) manually. It doesn't do anything particularly special, but it's very predictable. The existence of borrows in Rust lets you manually do +0 semantics without having to rely on the compiler noticing the optimization opportunity, although you do need to convince the borrow checker it's correct. [0]: https://github.com/apple/swift/blob/3b00177f768b630a8f7a11355c52f6d10eefa45b/stdlib/public/SwiftShims/RefCount.h#L239-L282 https://github.com/apple/swift/blob/3b00177f768b630a8f7a1135... [1]: https://forums.swift.org/t/a-roadmap-for-improving-swift-performance-predictability-arc-improvements-and-ownership-control/54206 https://forums.swift.org/t/a-roadmap-for-improving-swift-per...