Hacker News new | ask | show | jobs
by plq 775 days ago
You say:

> Rust `Arc` = C++ `std::shared_ptr`

GP says:

> Rust requires shared pointers (Arc) to also explicitly implement some sort of Mutex-equivalent runtime safety check in order to mutate the data.

Which is it?

> An example of a big C++ codebase using something similar is Chromium ...

Chromium's smart pointers are similar to their standard counterparts -- no mutexes for write access to pointed data.

Also, tangent but interesting: From https://www.chromium.org/developers/smart-pointer-guidelines...:

> Reference-counted objects make it difficult to understand ownership and destruction order, especially when multiple threads are involved. There is almost always another way to design your object hierarchy to avoid refcounting

1 comments

Both are true, Rust just has more restrictions. It’s not completely equivalent, but you can think of `Arc<T>` as `std::shared_ptr<const T>` as in if you use `unsafe` or `const_cast` you can bypass mutability restrictions. Otherwise to mutate you need another abstraction doing `unsafe` things for you, such as `Mutex`.

I mentioned Chromium because they also differentiate between thread safe and non-thread safe shared pointers.

If anything, Rust shared pointers are more similar to C++ std pointers because in Chromium the reference count is inside the class, which is very handy because you can reconstruct a smart pointer from a raw pointer (like `this`), at the cost of needing `T` to extend `base::RefCounted`.

Perhaps I am not making myself clear here:

- RefCounted: It's like shared_ptr but refcount load/modify/store operation is not atomic, thus not thread-safe. No synchronization for pointed data.

- RefCountedThreadSafe: It's like shared_ptr. This means refcount load/modify/store is atomic, so has overhead, yet safe to pass across thread boundaries. Again, just like shared_ptr, no synchronization for pointed data.

- Locker class above: It's an (incomplete) wrapper around shared_ptr where read-only access goes through a shared lock and rw access goes through an exclusive lock. I suppose this is what rust's ARC guarantees at compile-time with less overhead the sketch above?

So;

> Both are true, Rust just has more restrictions.

No, both are not true, my understanding of ARC ~= Locker && ARC > shared_ptr

I think that's where you're confused: `Arc` does not do any synchronization, again it's pretty much the same as `std::shared_ptr` (hence the name Arc: Atomically Reference Counted).

Your `Locker` does not do what `Arc` does, even at compile time, because it does not allow concurrent access, like an `Arc<AtomicBool>` would. Your `Locker` is more like an `Arc<RwLock<T>>`.

Best equivalent you can get in C++ is `Arc<T>` = `std::shared_ptr<const T>`.

https://doc.rust-lang.org/std/sync/struct.Arc.html

> Shared references in Rust disallow mutation by default, and Arc is no exception: you cannot generally obtain a mutable reference to something inside an Arc. If you need to mutate through an Arc, use Mutex, RwLock, or one of the Atomic types.

I guess you could get the final pieces to get something similar by creating `Send` and `Sync` traits in C++: https://doc.rust-lang.org/nomicon/send-and-sync.html. I think the main pain point here is that you cannot auto-derive `Send` and `Sync` so it would end up being very verbose.

FWIW, in C++11, a class C can similarly cooperate to enable reconstructing a shared_ptr from a raw one by deriving from std::enable_shared_from_this<C>.