Try X finally dispose of all resources. X, defer clean up X. Or how about just X and cleanup is done automatically, with the author of the resources deciding what is needed to clean X up.
There isn't any way to forget to drop a resource if you have RAII.
It's way easier to use, much clear, less typing, more predictable runtime behavior.
RAII... terrible name, great concept!
Traditionally langages with simpler runtimes simply used destructors for this, refcounting made it deterministic (modulo the old reference leak), but more advanced garbage collection schemes made that stop working.
RAII doesn't really fit into every language because they don't all have deterministic destructors/finalizers and objects with scoped lifetimes. Sometimes you only have one but not the other and you definitely need both.
And regarding sibling post about finalizers -- another broken concept as you cant use finalizers with limited resources (ex. graphic contexts, db connections, handles, locks, etc) and then these languages that use finalizers become complete mess if you need to manage such resources.
By similar I meant from the compiler's point of view. `try...finally` is the low level primitive to ensure some code runs at exit. If something throws an exception then destructors must run when unwinding the stack, which is what `finally` is for! But if you disable exceptions then `try` blocks are probably all basically no-ops.
For example, combining RAII with lock free data structures, which usually ends up in techniques like hazardous pointers instead.
It's also more exception-safe when you have more than one throwing call in the try block.
One can just wrap os.Exit in a helper to get the expected behaviour.
I certainly wouldn't mind if modern servers had something similar to make sure programs could stop gracefully. But then that doesn't help if you have a hardware failure...
[0] https://learn.microsoft.com/en-us/windows/win32/api/fileapi/...
[1] https://devblogs.microsoft.com/oldnewthing/20170510-00/?p=95...
An unresolved promise that causes other code to be skipped while program execution continues after the block is, well, unintuitive to say the least.
``` async function run() { try { await new Promise(() => { // The executor function finishes, // but it never calls resolve() or reject(). }); } finally { console.log("cleanup"); } } ```
I never expect cleanup to be logged.