top | item 31538645

(no title)

clappski | 3 years ago

In practise shared_ptr should only be used when you need to actually share ownership of some dynamically allocated object. You get around it by using it in the right place. You’re correct on the refcounting being a performance issue.

Codebases that use shared_ptr for everything because they think it ‘solves’ having to manage memory are smelly, it shows that no one has thought about the ownership model. Not to say there isn’t a solid use case for it, but some developers use it by default because they don’t fully understand the trade offs/why it’s important to think about ownership of managed memory.

As an example in our medium to large code base we have two places we use a shared_ptr, completely off the hot path and for long lived objects that aren’t copied more than a few times, but need to share ownership of the pointee - in a similar use case of your use of Arc.

Almost all of the time unique_ptr, with a single owner, is exactly what you want to replace the memory management side of a dynamically allocated object. From there you will pass around raw pointers or references to the object managed by the unique_ptr (for functions that need to ‘borrow’ the object).

Something interesting about shared_ptr is how it relates to weak_ptr, although I’ve never seen weak_ptr in anything out side documentation!

discuss

order

pimeys|3 years ago

Isn't `weak_ptr` the same as `sync::Weak` in Rust? There are a few places I've seen this in use. One of them is if you want to build a tree of data you can traverse in any direction. E.g. parent -> child and from child -> parent again. One of the directions must hold a `sync::Weak`, otherwise we leak memory.

We've been going around this issue by storing flat collections instead of structuring the data as a tree. We for example have parents and children in adjacent vectors, and parent only needs to know the ids in the children vector to be able to iterate over them. In this case you can do a data structure storing the id values that are `Copy`, meaning this whole data structure can be `Copy` and is super easy to reason about.

The other use-case is a database connection pool. When you take a connection out from the pool, you get a reference to the pool itself. The pool is quite often internally inside an `Arc`, and the connection gets wrapped into a guard object holding a `Weak` to the pool. With RAII when the connection drops out from the scope, the `Weak` is checked is it still valid (is the pool alive) and if so, the connection is returned back to the pool.