Static lifetimes are also a large part of the rest of Rust's safety features (like statically enforced thread-safety).
A usable Rust-without-lifetimes would end up looking a lot more like Haskell than Go.
Static lifetimes are also a large part of the rest of Rust's safety features (like statically enforced thread-safety).
A usable Rust-without-lifetimes would end up looking a lot more like Haskell than Go.
RAII works only for the simplest case: when your cleanup takes no parameters, when the cleanup doesn't perform async operations, etc. Rust has RAII but it's unusable in async because the drop method isn't itself async (and thus may block the whole thread if it does I/O)
Also, if your cleanup takes parameters, you can just store them in the struct.
When dealing with async operations they tend to end up at a network boundary and thus the service enters distributed system land.
Now the async drop also has to handle the server crashing before the drop happens and at any time when it happens. Keeping that in mind trying to actually drop something becomes quite meaningless since you need to handle all other cases either way.
Personally I love Rust’s `fn foo(self, …)`, which is just like a regular method but consumes the value.
Deallocate by default is fine, but sometimes you need to run specific destructors (linear type style). I’ve long wished for an opt-out from implicit drop semantics for resource/handle types.
[0]: https://play.rust-lang.org/?version=stable&mode=debug&editio...
Thus the complexity of handling memory is greater than that of other resources and the consequences of getting it not 100% right are frequently worse.
Has anyone build a collector that tracks multiple types of resources an object might consume? It seems possible.
The problem is that things like closing a socket are not just generic resources, a lot of the time nonmemory stuff has to be closed at a certain point in the program, for correctness, and you can't just let GC get to it whenever.
Originally, they used pure reference counting GC, with finalizers used to clean up when freed. This was "fine", since RC is deterministic. Everything is freed when the last reference is deleted, nice and simple.
But reference counting can't detect reference cycles, so eventually they added a secondary tracing garbage collector to handle them. But tracing GC isn't deterministic anymore, so this also meant a shift to manual resource management.
That turned out to be embarrassing enough that context managers were eventually introduced to paper over it. But all four mechanisms still exist and "work" in the language today.
For Python in general, no. For example, as far as I know Jython reuses the JVM's GC (and its unreliable finalizers with it).
It's also easy to introduce accidental cycles. For one, a traceback includes a reference to every frame on the call stack, so storing that somewhere on the stack would create an unintentional cycle!
Now I use close() methods for anything that needs to be closed. If I mess up and there's some obscure bug, hopefully GC will fix it, but it seems too brittle and easy to make mistakes with to rely on.
Why is that a "problem with GC"?
Abstracting away >90% of resource management (i.e. local memory) is a significant benefit.
It's like saying the "problem with timesharing OS" is that it doesn't address 100% of concurrency/parallelism needs.