3 ms·
IMO, the author is looking at the central problem with GC systems: non-deterministic destruction, and unclear ownership. > This trivial example has deeper impl
by deathanatos 6y ago
IMO, the author is looking at the central problem with GC systems: non-deterministic destruction, and unclear ownership.
> This trivial example has deeper implications. If one accepts this practice, then one must also accept that storing a file handle anywhere, such as on an instance, is also disallowed. Unless of course that object then virally implements it owns context manager, ad infinitum.
Yes, and I just accept that. This happens in C#, too[1]:
> If your class owns a field or property, and its type implements IDisposable, the containing class itself should also implement IDisposable. A class that instantiates an IDisposable implementation and storing it as an instance member, is also responsible for its cleanup.
And for good reasons; in this regard, the languages are basically identical. I'd say any language that uses GC and instead has some sort of language-based guard system (Python's `with`, C#'s `using`) or even just through convention (e.g., Java, back before try-with-resources) will end up having that happen. Depending on the GC to collect these resources isn't wise, since no behavior on the GC is actually guaranteed.
> No, it will not be fixed. This version only appears to work by not closing the file until the generator is exhausted or collected.
I'd call that fixed; the file is properly and deterministically closed, when the generator is exhausted or manually closed. (By calling the generator's `close()` member function.) Now, it is now up to the calling code to remember at every use site to do that, and it should be made an explicit part of the documentation for the interface. But, the same for context managers, or things having a close: it's part of the contract that you will call that .close(), or wrap it in a with block, or the resource leaks until the GC gets to it. (I do wish generators were context managers, to make this easier.)
IME, the bigger problem here is that GC forces, for resources that have a destructor beyond "free the memory" (that is, what the GC itself handles), for each call-site to remember to clean up the resource. Files are one thing, and generally, yes, you'll probably get lucky and not run into issues. Locks are more fun, and often leaving it to the GC to release a lock is a lot more painful. (Then there are threads, child processes, sockets, …) RAII fixes this for all types of resources by bounding the lifetime of the resource to the scope(s) that it lives in.
(And, because it inevitably comes up, IME, people worry too much about cycles; cycles do happen, but are incredibly rare compared to non-memory resources (those that are more than just memory, e.g., files, locks, etc.), and vanishingly rare compared to all resources (those that are more than just memory and those that are just memory, like vectors or strings). Yes, they are an issue of sorts in RAII, but there are ways to handle them. I do not think the bad with cycles outweighs the good of RAII.)
[1]: https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose#cascade-dispose-calls https://docs.microsoft.com/en-us/dotnet/standard/garbage-col...
- dnautics 6y agoIt's not a problem with gc in general. Erlang for example you typically don't close files if you know the lifetime of your process is transient, you do, if it's permanent. End of rule.
- jjnoakes 6y agoIs that really idiomatic? I don't write much erlang but it seems like that wouldn't work for any code shared between two processes, one transient and one permanent.
- tsimionescu 6y agoWell, RAII 'fixes' the problem by making it uniformly complicated, instead of uniformly simple. For memory resources, there simply is no ownership in GC-land. This frees you to use patterns that you just can't implement in RAII-only. Famously, atomic compare-and-swap for heap-allocated objects is much harder to implement with manual memory management (who owns the old pointer after a successful CAS?)[0]. And sure, situations where ownership is not clear are rare [1], but so are file handles that aren't immediately closed, so in practice GC + local RAII seems to cover a huge amount of cases. [0] https://www.drdobbs.com/lock-free-data-structures/184401865 https://www.drdobbs.com/lock-free-data-structures/184401865 [1] Rust could work as a case study here: how many places require `unsafe`?