Memory Ownership

I recently dabbled in Rust and noticed that the explicit nature of ownership (Box pointers) really helps tracking ownership better than the implicit model (regular pointers for both owning and non-owning pointers) used in C. This comes with drawbacks, namely making easy things very difficult to do with regular pointers and wrestling the borrow_checker for obviously correct programs.

I also re-watched Jonathan Blow's original programming language discussion where he also brought up the idea of owning pointers. I spoke to him about it, and he says it is still on the table for his language JAI but no guarantees.

I was curious what the more experienced users on the forums think of the idea of explicit ownership semantics being a core part of a language.

If you, like me, view Rust's approach too restrictive, what ideas do you have to improve (relax) their model? Obviously, any suggestion wouldn't be as fool-proof as Rust, but there must be some reasonable comprises while deriving most of the benefit.



Edited by novus1044 on
You can't really go deep into this discussion without mentioning RAII

In C++ it's kinda horrible with their rule of 0/3/5 (or 4). Where if you write a destructor you need to add a copy constructor and a copy assign. Then you have the option of adding the move variants. The move and copy assignments can be replaced by a value assignment. That's the kind of thing to keep in mind when writing an owning object in C++.

But you can make programs with deep ownership graphs without ever needing to write a destructor by using std::unique_ptr and std::vector to keep the objects.

Then there is the issue that you can potentially blow the stack on destruction of the root because all the destructions happen recursively.
I would imagine that wrestling with the borrow checker is something that beginners of the language have to deal with far more than people who use it regularly. When you get to the point when the language comes naturally, you'll probably be writing much safer code at a small cost. Seems worth it from here.

I've only started dabbling with Rust myself, so I'm not sure how long it will take for it to come naturally or if it will get to a point that it's worth the overhead. I just have my suspicions.
I agree with you CapatainKraft that the borrow checker does become less burdensome over time. Also, to Ratchet's point, I think that the whole RAII and deterministic destruction has become so commonplace that when ownership is talked about it comes with a lot of *potentially* unnecessary baggage.

Just to clarify my previous post, I want to provide an example of my line of thinking. In Rust you have the two reference (pointer) types &T and &mut T that handle read/write privileges of the pointers. I was thinking what if you mirrored the semantics of &T and &mut T with respect to ownership.

Say for the example that a basic pointer is * and an owning pointer is *!

Rust example with &T and &mut T:
fn some_function1<T>( x: &T, y &T ) -> T { ... } // data pointed by x and y will be read
fn some_function2<T>( x: &T, y: &mut T ) -> T { ... } // data pointed by x will be read, y written

JAI example with *T and *!T:
some_function1 :: ( x: *$T, y: *T ) -> T { ... } // x and y will not free the data
some_function2 :: ( x: *$T, y: *!T ) -> T { ... } // x will not free the data, and y will

For the owning pointers you could do what Rust does and disallow the use of the previous owner once you assign an owning pointer to another owning pointer. You would also disallow basic pointers to be assigned to owning pointers. Also, you could prevent free/delete from being called on a basic pointer.

These rules wouldn't alleviate all the problems with memory ownership. You as the user would still have to manually free the memory of an owning pointer. Also, dangling pointers could still be a problem with multiple basic pointers pointing to freed memory. At the very least it would help you better gauge where something went wrong. It also provides a cleaner interface as to how the pointer is used across the function boundary. Finally, it would make you think more explicitly about ownership and possibly prevent you from making these errors in the first place.

Hopefully, this clarifies my last post and doesn't sound too stupid = ).







Edited by novus1044 on
Steve, I think that the sheer number of & and &mut would be totally context dependent. I would suspect a program with a lot of mutation would use raw pointers (*mut T) like Redox does. I personally don't have enough experience in Rust to say either way though.

Also, your comment about the use of unsafe in Redox is kind of what I am getting at. If you dip into unsafe you are back to C. So you either have these extremely strict guidelines, which in the case of Redox are very difficult or impossible to get around, or you go back to the bare bones of C.

I am probing to see if there is a middle ground where we get some assistance from the compiler to avoid making silly mistakes.

When I first learned Rust I was kind of put off from all the duplicate code, one for a reference, one for a mutable reference, and one for owned. With the stuff I discussed before you could skirt this slightly by using function overloading based on the parameter type (owned pointer vs basic pointer).




Edited by novus1044 on