Hacker News new | ask | show | jobs
by edflsafoiewq 2378 days ago
> Every time I want to write a class that manages a resource with RAII, I am almost always writing a copy constructor, move constructor, assignment operator, move assignment operator, and destructor.

No you aren't. You only do that at the lowest abstraction level. For normal classes you can add a vector member and it will just work (see "rule of zero").

> On the other hand, if I write the equivalent code in Go or Nim, I can just add something like a Close() method.

Yes, and then find and update every place in the code where that class is instantiated. If the class was ever instantiated as a temporary, you also need to pull it out into a named variable so you can use defer. If the class was ever used in a vector, you need to find all the places where an element of the vector is removed and make sure close is being called there, etc.

This is really awful, so in a language without RAII, you are strongly incentivized not to tie ownership to object lifetime. I've found the best way is to aggressively simplify managed lifecycles and do allocation/cleanup in some kind of manager object.

1 comments

> No you aren't. You only do that at the lowest abstraction level. For normal classes you can add a vector member and it will just work (see "rule of zero").

That’s what I mean when I write “a class that manages a resource with RAII”. Just on a personal note—it can be damn frustrating writing a comment on HN sometimes, because somebody will always find a way to misinterpret it completely.

> Yes, and then find and update every place in the code where that class is instantiated. If the class was ever instantiated as a temporary, you also need to pull it out into a named variable so you can use defer. If the class was ever used in a vector, you need to find all the places where an element of the vector is removed and make sure close is being called there, etc.

Once you step away from automated formal verification, which is very rare to begin with, the programmer is always responsible for maintaining some nonzero number of program invariants. In my years of experience with Go, the manual work to close file handles when you are done is not particularly burdensome or difficult, and I have seen very few errors slip into production related to resource management which would have been solved by RAII.

> This is really awful…

I would be interested to understand what design decisions led to the experiences you describe, because I have never seen a project suffer from those problems. Can you give more details?

> Once you step away from automated formal verification, which is very rare to begin with, the programmer is always responsible for maintaining some nonzero number of program invariants.

Of course! It's nice to have all the help with that you can get.

> In my years of experience with Go, the manual work to close file handles when you are done is not particularly burdensome or difficult, and I have seen very few errors slip into production related to resource management which would have been solved by RAII.

Go has a GC which greatly reduces the number of things to manage, but the discussion is about Zig. In Go, if you add, let's say, a string to a class, you don't have to do anything, it will just work because of the GC. In Zig, if you add a string to a class and it didn't already have a close method for some other reason, you now have to update all the users. Essentially its unreasonably burdensome to switch from not managing a resource to managing a resource unless there are few users.

> That’s what I mean when I write “a class that manages a resource with RAII”. Just on a personal note—it can be damn frustrating writing a comment on HN sometimes, because somebody will always find a way to misinterpret it completely.

This wasn't some kind of pedantic point-scoring. In something like C++/Rust every single class that so much as contains a string manages a resource with RAII, but only a tiny number of them actually require you to write any cleanup code. In a language without RAII, if you used the same design, a huge number of them would require writing cleanup code.

> Can you give more details?

The sort of design I prefer in a language without "smart-objects" is along these lines: https://floooh.github.io/2018/06/17/handles-vs-pointers.html