As an example of why this matters, I’ve got a class called TimeCapture in a project. If I want to instrument how long a block takes (full function, inside of an if, etc) I can add `TimeCapture tc(“my-metric-name”);` to the block and know that the destructor on that object will be called precisely at the end of that block (capturing the end time of the block)
I don’t know the rules for Swift or Ada or OP, but I’m guessing they’ll vary.
Not true. They run when the object is deallocated. In CPython if the object is not involved in or referenced from a cycle, that’s as soon as it goes out of scope: the ref count goes to 0 and the object gets deallocated.
This is trivial to check: create a structure with a noisy __del__ and create an instance from a function, call the function, del will run. The GC has no reason to run at this point, there’s no allocation requiring any sort of reclamation, but if you don’t trust that you can just `gc.disable()` and observe… the exact same behaviour.
That's a CPython implementation detail, not a guarantee that you can rely on.
For example, both PyPy[0] and Jython use tracing GCs where this is not true.
[0]: https://doc.pypy.org/en/latest/cpython_differences.html
Which would be why I specifically wrote « in CPython ».
And it still makes the original assertion completely incorrect. Python destructors do not run « down the road, during the next GC cycle ». They may, in certain cases or implementations.
> implementation detail
There is no guarantee that even CPython will keep the current behaviour. Ultimately, the promise you're given is that __del__ might be invoked by the system some point between "the object has no live references" and "never".
The original assertion gives a much closer intuition than your description of the current behaviour.
The original commenter made a specific assertion. I demonstrated that their assertion is not true.
It seems to me that there must be some other language that would have the c++ guarantee, but I don't know of it. (it isn't hard to implement, and c++ is well known enough that other languages designers can consider the pros and cons and surely someone else has decided it is worth it)
Rust's Drop[0] has basically the same guarantees as C++: the destructor will run right before the memory is freed (although it is possible in both cases to also leak both). Combine with Pin[1] if you need the exact memory location to be stable as well.
> it isn't hard to implement
Sadly, it's basically incompatible with tracing garbage collectors, since they make it completely unpredictable when the destructor will run.
So if you ever want to allow yourself to switch to tracing GC then you'll either have to disallow it completely, or bury it in a hidden corner of the docs with a load of caveats (like Python or Java). And in the latter case you'll still have people like the GP stumble upon it, say "oh, this looks neat", and set up a really painful trap for their future selves.
[0]: https://doc.rust-lang.org/stable/std/ops/trait.Drop.html
[1]: https://doc.rust-lang.org/stable/std/pin/index.html#drop-gua...