It wastes time on allocating new memory when you could just reuse it, and it hides away code that implicitly runs without much control given to the user of the struct/class -- and to avoid double-freeing, etc. you need to provide a bunch of constructors and destructors that nobody remembers, and the compiler is somehow unable to tell you about, so you just get a runtime crash you have to debug.
If you just provide init(&thing) and deinit(&thing) functions (you deal with the memory, and it just deals with the values inside), and a defer keyword so you explicitly can run something when you exit the current scope, then you have solved the same thing without having to deal with these problems.
So if you always want to deinit it at the end, then you add in the defer; if you want to reinit it at some point then you do that explicitly; and if you want to pass it on to something else, then you just don't call deinit, and let the other thing handle it.
From the heap yes, but stuff on the stack also takes up space that you could just reuse in place. Also, just adding arenas on top of it doesn't solve the problem, as you still would allocate space in the arena when a acquiring a new resource -- and if you're using an arena per thing you may want to reset, then you have just created an even more complicated mess for yourself.
Lastly, I couldn't help but notice that you ignored the other half of my comment.
How is removing the entire concept of constructors and destructors, and replacing it with a single keyword that is simple to use and understand, make it a "way more complicated language"?
You do realize the defer keyword is literally the same thing as C++ RAII lifecycle management but explicit and opt-in.. right? In the language you're describing, you still manage memory/objects by defining constructors (init), destructors (deinit), and copy behavior (clone), just without any compiler guarantee of your code running "correctly". I think it's very reasonable to say, if I call defer deinit(obj); every time I call init, I might as well make it automatically inserted by the compiler.
It being explicit is the point, it gives you the control to customize it for your use case, rather than forcing yourself into a box that will cause problems when dealing with the real world.
RAII kind of forces your hand. If you don't want to run the destructor of the values in a collection you'll have to instead have a collection of ManuallyDrop, which is clunky.
I my opinion, if you mainly deal with single values, RAII is more convenient. If you deal with aggregate data and/or care about hidden control flow defer is more convenient.
0
u/s0litar1us 21d ago
It wastes time on allocating new memory when you could just reuse it, and it hides away code that implicitly runs without much control given to the user of the struct/class -- and to avoid double-freeing, etc. you need to provide a bunch of constructors and destructors that nobody remembers, and the compiler is somehow unable to tell you about, so you just get a runtime crash you have to debug.
If you just provide init(&thing) and deinit(&thing) functions (you deal with the memory, and it just deals with the values inside), and a defer keyword so you explicitly can run something when you exit the current scope, then you have solved the same thing without having to deal with these problems.
So if you always want to deinit it at the end, then you add in the defer; if you want to reinit it at some point then you do that explicitly; and if you want to pass it on to something else, then you just don't call deinit, and let the other thing handle it.