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

*There are no “objects” in the OOP sense, and as such you can say it has “no constructors and no destructors”, but Rust doesn't have “objects” either and neither does it has constructors, yet there's zero doubt Rust is using RAII for memory management (Rust has destructors though, but they aren't generally being used for “memory management” but for broader “resource management”, like network sockets or file descriptors).

If you really don't want to call them objects, there are at least “items” that are “created” and then “deallocated” based on their lexical scope, which is exactly what Rust does too for its RAII.



There are a few misconceptions in your comment that are very common, so I hope you'll allow me to clarify them and in the process hopefully explain why this sort of "parallel stack on the heap" approach is not the same as RAII at all.

Lets ignore the horrifically named C++ feature for a moment and speak more generally of "ownership".

Ownership (in the domain of programming languages) is kind of relation between resources. If resource A is the sole owner of resource B, then relinquishing resource A also relinquishes resource B, but not vice versa. It gives a form of agency to resources: "Hey A, it's your job to take care of B alright?"

Note that there's nothing about "lexical scope" in the description above. In fact, tying ownership to lexical scope isn't necessary at all. For example, I could do `new std::vector<std::string>()` in C++, fill it up with a bunch of strings, and then call `delete` on it at my leisure. No lexical scope involved.

Tying freeing to lexical scope is a convenience, a way to automate the freeing of resources, but it's not required by "ownership semantics" at all. In fact, the idea that freeing is tied to "lexical scope" is incorrect even when considering the automation provided by Rust and C++.

Both Rust and C++ have the concept of "moving". If an object is moved, then its lifetime is now tied to a different scope (if it was returned for example) or to another resource.

If you move a resource in C++, it's destructor at the end of scope will be a no-op. If you move a resource in Rust, it won't add a destructor call at the end of scope at all. If the move was conditional (only happens on a subset of branches) rust will actually add a little boolean variable tracking this, and then check that variable before calling the destructor at the end of the scope.

This last bit is actually only necessary because Rust wants to tie the freeing of the resource to end of scope. Freeing at end of scope isn't necessary at all. Rust could free the resource right at the point of last use, the language has excellent lifetime tracking it could use for this.

The reason Rust places the call to the destructor at the end of the scope is to make unsafe less unsafe. If there's a raw pointer to that data and it might get deleted at any point then, well, that's very dangerous. If you know the free will only happen at end of scope, then you can work with that.

So what do I want to get across with all of this? Ownership (RAII) is a way to tie resources together that is only incidentally related to lexical scopes. It gives resources agency by allowing them to manage other resources, via custom destructors.

What C3 has (for memory management only, it uses defer for other resources) is just a way to have a parallel stack on the heap. There's no "ownership" in the same way the stack isn't really an "owner" in the same sense as other resources. There are no destructors, data is inert.

All allocations that happen within that parallel stack are freed in bulk (not one by one, a single big free), because the only real "resource" is the parallel stack itself. You cannot "move" this parallel stack, and is not managed by another parallel stack, there's no ownership.

If you want the same functionality in Rust you need to use a crate like Bumpalo. In C++ you'd use std::pmr::monotonic_buffer_resource. RAII is strictly more powerful since you can model these parallel stacks in it, but it's also not equivalent since you can't have cycles without implementing this first.


Thanks a lot for your excellent comment.


If you're interested, the Austral language (not my creation, but I'm a fan of the author's work) has ownership semantics where freeing is always explicit and in the hands of the programmer, but it's still safe like Rust.

Because both the destructor calls and the "borrowing" scopes have to be written explicitly in the language, it really helps in understanding how it all fits together.


Thanks for the pointer.




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

Search: