Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I'm not sure how RAII could ever need more code, when the only difference is that functions get called automatically (instead of through close(), or a close() inside a defer).


There’s way more going on with RAII.

With RAII, you’re expressing ownership in code. That means that you have to write all the extra code to track that ownership in the type system or at runtime. 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.

On the other hand, if I write the equivalent code in Go or Nim, I can just add something like a Close() method. Because ownership isn’t expressed in code, there is a higher chance to make mistakes, but the code is simpler.


If you need a deep copy you also need to write a copy() function for the object without RAII. (Although one could argue that that is still RAII, just without operator overloading.) But in any case, for deep copies, the copy code needs to be somewhere.

If you don't need a deep copy (which honestly you basically never need once you already have some container classes), you can pretty much always just use an unique_ptr or write a unique_handle class or something to that regard, which means you never need to write any of the operators.


If you just want a unique_ptr, then you should at least write the code to delete the relevant operators or make them private. Boilerplate, yes, but that is how idiomatic C++ code is written.


I'm not sure what you mean. If you have code like this

    struct Foo {
        unique_ptr p;
    };
Foo is automatically not copyable. That code is about is perfectly good idiomatic C++, with zero boilerplate. And that is how most classes actually end up looking in idiomatic C++, there's almost never the need to implement operators.


> 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.


> 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




Consider applying for YC's Winter 2026 batch! Applications are open till Nov 10

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: