4 ms·
I'm not a big fan of RAII because it falsely conflates lifetime with reachability. This assumption breaks under a number of circumstances, e.g.: * Closures cap
by rbehrends 11y ago
I'm not a big fan of RAII because it falsely conflates lifetime with reachability. This assumption breaks under a number of circumstances, e.g.:
* Closures capturing their environment and keeping local variables alive longer than intended.
* Debugging code keeping a record of objects.
In either case, you have likely failure modes in that either the resource can be deallocated prematurely and then accessed through a stale reference or that it is kept around longer than its intended lifetime.
RAII also does not provide a solution to a number of resource management scenarios, such as requiring access to an unbounded number of resources of which only a finite number can be reified at any time (e.g. due to the maximum number of file handles a process can manage). The obvious solution – a caching mechanism that will disconnect a resource from its in-memory representation upon eviction and reconnect when it is being used again – needs to attach itself to resource usage, not resource creation and destruction.
Most scenarios where RAII is being used are either trivially handled in any language that supports closures or macros, or benefit from explicit reasoning about resource lifetime, anyway.
Speaking of closures, I think it's no coincidence that RAII first arose in a language that until recently had no such concept; closures both create problems for RAII and also obviate many common use cases.
- mlvljr 11y agore: * Closures capturing their environment and keeping local variables alive longer than intended. How can that happen to a stack variable (and RAII is about those), given C++11/14's closures ("lambdas") do not prolongate the lifetime of the objects captured by reference? Or did you mean capture by value? (would be really confusing with RAII-ing classes :) )
- unscaled 11y agoProperly executed RAII, at least in C++, should never break in the two cases you mention. The RAAI object should be non-copyable (no copy constructor in C++), so the only way it can be passed by value to a closure is with an explicit move operation, which is not something you would do on mistake. The original copy would now revert to a proper "uninitialized" or "closed" state. Debuggers too are usually well aware of out-of-scope objects, so I don't see the point here. As for complex scenarios, they are probably less than 1% of the time you'd use RAAI. When you do have complex scenarios that need techinques like caching and pooling - well, yeah, RAAI won't solve them on its own, but the same goes for macros and closures. Besides that, most RAAI uses just can't be replaced by closures, since you cannot break a loop or return a value from within a closure, for instance (Ruby procs can do that, but there's no equivalent to that in most languages). And both a closure-based and a macro-based solution quickly become an unwieldy nest-fest once you need to deal with multiple resources in a row.
- rbehrends 11y ago> Properly executed RAII, at least in C++, should never break in the two cases you mention. So, std::shared_ptr (or anything else that does reference counting) is not properly executed RAII? > Debuggers too are usually well aware of out-of-scope objects, so I don't see the point here. I wasn't talking about debuggers, but code that is enabled for debugging purposes. E.g. globally remembering the last N objects passed to a function. My larger point is that either lifetime or reachability is off. You either have a resource that can still be accessed through a stale handle even though it's been deallocated or one that remains allocated past its intended lifetime.
- kentonv 11y ago> closures both create problems for RAII and also obviate many common use cases. As someone who uses a lot of closures and a lot of RAII, together, I can assure you this is not the case: - C++ closures are very explicit about what is captured and how (by reference, by copy, by move/ownership transfer). It actually works incredibly well with RAII. - Closures as an alternative to RAII only works in a small subset of use cases: cases where the resource lifetime is bound to a stack frame. They do not easily model resources owned by objects, nor (ironically) resources used in an asynchronous callback context. BTW, Rust actually statically guarantees no dangling pointers -- without using GC. And yes, it has closures.