|
|
Subscribe / Log in / New account

Herb Sutter on increasing safety in C++

Herb Sutter on increasing safety in C++

Posted Mar 14, 2024 1:39 UTC (Thu) by NYKevin (subscriber, #129325)
In reply to: Herb Sutter on increasing safety in C++ by dvrabel
Parent article: Herb Sutter on increasing safety in C++

> Arc::into_inner() may or may not get the inner value. If the program is expect()'ing to get the inner value but doesn't because some other part of the program is holding a reference it shouldn't, the program will behave incorrectly.

This is an unreasonable expectation. Arc::into_inner() is a highly specialized interface. It is not designed for general use. It *should* be hard to use correctly.

Before I explain how this interface should be used, we need to take a step back and talk about the actual problem that it is trying to solve. You have an object behind an Arc; let's say it's Arc<Foo> (for some type Foo). There are several different reasons you might plausibly want to use this function:

* You want to mutate the Foo instance. Then call Arc::get_mut().
* You want to dispose of something owned by the Foo when the refcount drops to zero. Then implement Drop for Foo, and write the code in its drop() method.
* You want to replace the Foo instance with a different Foo instance. Then call mem::replace() or mem::swap() on the &mut Foo that you can get out of get_mut().
* You want to do one of the above, even if another thread might be using the Foo instance. Then wrap the Foo in a sync::Mutex or sync::RwLock.
* You want to take the Foo object out of the Arc and consume it. Then use an Arc<Option<Foo>> (plus a lock, if desired) and call Option::take().

All of the above are straightforward to use correctly and have no problems with silently doing the wrong thing. Here is the actual use case for Arc::into_inner():

* You want to take the Foo object out of the Arc and consume it, you don't want to do that until it is no longer in use, you don't want to write code for the case where the Foo has already been consumed, the code that consumes the Foo can't feasibly live in Drop::drop(), *and* you know that all clones of the Arc are owned by worker objects or worker threads, which all know how to consume the Foo at the end. Then all you have to do is ensure that every function which takes an owned Arc<Foo> either consumes it with into_inner() or passes it along to another part of the worker logic, and that it is never permanently stored in some long-lived data structure. And yes, that is difficult, but as mentioned, all of the simple use cases already have another solution which is easier to use and just as performant (or nearly so).


to post comments

Herb Sutter on increasing safety in C++

Posted Mar 14, 2024 10:12 UTC (Thu) by dvrabel (subscriber, #9500) [Link] (1 responses)

Arc::get_mut() also returns an Option if you'd rather I'd used that as an example.

If I have a T I can get a &mut T only if the compiler can prove that it is safe to do so.

If I have a Arc<T> I can only get a &mut T if the runtime check of the reference count says it is safe.

Herb Sutter on increasing safety in C++

Posted Mar 14, 2024 16:59 UTC (Thu) by NYKevin (subscriber, #129325) [Link]

As I explained, you need to use sync::Mutex or sync::RwLock for that to work. Arc::get_mut() is for cases where you've just constructed the Arc and have not yet shared it.


Copyright © 2025, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds