← Back to context

Comment by snovv_crash

3 years ago

Memory isn't the only resource that needs management. File handles, network sockets, locks, security codes and more, all need to have deterministic lifetimes. If you language doesn't support lifetime management via some mechanism then all of these things are the next part that will bite, even if you have GC for memory.

In .Net/C# this is done with IDisposable and the using statement which works quite well.

Ex:

  using (var sr = new StreamReader(filename))
  {
     txt = sr.ReadToEnd();
  }

https://learn.microsoft.com/en-us/dotnet/api/system.idisposa...

While I do love Rust-style resource management, Java’s try-with-resources blocks plus cleaners are not particularly worse.

  • Try-with-resources is tied to a lexical scope. C++/Rust RAII is not, you can move the resource e.g to another thread after initialization, and that thread can outlive the scope that created the resource. Also C++/Rust allows shared ownership through recounted references, something try-with-resources can't do.

  • Cleaners aren't deterministic, so that's meaningfully worse than rust's resource mgt. Java's try with resources cleanup only works if the resource is freed up before the callstack is unwound. Currently that's only viable for the most trivial of scenarios. Loom will make that applicable in more places but still not as widely applicable as a borrow checker/rc based deterministic resource management.

  • What about if you want your resource to be longer lived, but still safely disposed of in a timely manner when it goes out of scope, for example a port in a webserver class? Do you have to keep the stack alive here to keep the resource, or can you put it into an object somewhere to get a more RAII style resource management?

  • Could a Java version of the future maybe also manage memory with a try-with-resources block ?

    • Your wish is granted :D

      https://openjdk.org/jeps/424

      Though there was nothing inherent to ever block you from doing it (just create an object that implements Closeable, and malloc in the constructor, free in the close method), this new API makes it quite great. The basic API gives you a SegmentScope which denotes a lifetime, and MemorySegments that point to a memory region (pointer+size+layout+scope). MemorySegments have both spatial and temporal safety, so accessing memory out of boundary will only throw an exception instead of corrupting memory, while any access outside the lifetime of the scope is forbidden. Oh, and they are also thread-safe, only optionally being shared between threads.

      So in practice it you can write something like

         try (var arena = Arena.openConfined()) {
           MemorySegment segment = arena.allocate(someLayout);
           // use segment
         } // memory will be deallocated here

      2 replies →

    • Java and the JVM has a garbage collector, and will likely always have, and GC makes memory management a lot simpler and safer, so while you could use try-with-resources for some kind of memory related resource, it likely would not be to make things safer, but to make things less safe and faster, and if you start doing that you will have significantly reduced safety compared to rust I think.

System languages with GC like D (one example among many others) support RAII for resource management.

This gets missed a lot. Rust isn't memory safe, it is resource safe.

  • It's funny that people are assuming I was talking about Rust when I was actually referring to modern C++. RAII is a powerful concept.

    • It is, I've written c++ as my primary language for 30 years. I skipped c++14 and I'm still using 17, but staying up to date other than that. But RAII doesn't come anywhere close to the borrow checker in the number of mistakes it prevents.