4 ms·
The whole ownership and borrowing system is a type of automatic memory management. It just happens at compile time instead of runtime like most automatic memory
by brokencode 4y ago
The whole ownership and borrowing system is a type of automatic memory management. It just happens at compile time instead of runtime like most automatic memory management systems.
Manual memory management is possible in Rust, but is not typically something developers need to interact with. There is typically no need to manually free objects when you are done with them. You just let the system destroy the objects automatically when they go out of scope.
- Calavar 4y ago> There is typically no need to manually free objects when you are done with them. You just let the system destroy the objects automatically when they go out of scope. By deciding where to scope the variable with the destructor/drop function, you've already made a manual decision about memory management. The compiler implicitly inserting a call to the destructor does not automate the decision of when/where to allocate or free memory - its just syntax sugar over the decision that you already made. This is just as true of Rust in 2023 as it was of C++ 40 years ago. With true automatic memory management like tracing GC or reference counting, you have no idea where the or when the memory will be freed as you write the code, and the answer will usually be different over different invocations of the same code. > The ownership and borrowing system is a type of automatic memory management No, it isn't. The borrowing system is completely orthogonal to memory management. You can write a function that takes a borrow, do all sorts of things with that borrow, including forwarding it along to other functions further down the call chain, and the memory backing that borrow could be statically allocated, dynamically allocated with the default rust allocator, or allocated by some custom solution like a slab or pool allocator. The code reads the same regardless of the memory management scheme because you make the decision on how you will allocate (and eventually free) the memory before you ever create a borrow. The borrow checker can help keep you from making use-after-free errors, but it doesn't dictate when, where, or how memory is freed. That's still up to the programmer.
- kortex 4y ago> By deciding where to scope the variable with the destructor/drop function, you've already made a manual decision about memory management. That's not really what's meant by manual memory management. MMM almost always means things like malloc/free, arenas, buffers with pointers, etc. I would not call stack allocation "manual", and IMO Rust's system is closer to stack allocation than it is to either manual or automatic memory. In terms of DX, bugs and ergonomics, you only really have two schemes: ones which requre some kind of free() call and thus permit use-after-free bugs (manual) and those that make it impossible (ignoring weakref and friends). Rust is not manual by virtue of no free() instruction. In the same breath, "automatic memory management" is not an exact antonym of manual.
- brokencode 4y agoEven in languages with a garbage collector you make decisions that impact the lifetime of an object. That is not what makes the memory management manual. For instance, you can still have a memory leak in Java if you maintain a reference to an object that you never intend to use again. It’s still up to the programmer to prevent this. > With true automatic memory management like tracing GC or reference counting, you have no idea where the or when the memory will be freed as you write the code, and the answer will usually be different over different invocations of the same code. This just isn’t true, and it would be anarchy. You know that the object will be freed sometime after the last reference to it dies. This is the whole reason to use automatic memory management. Reference counting approaches typically go even further and free the memory immediately when the last reference is eliminated. And this is basically what Rust does with ownership. When the object goes out of scope and ownership is not transferred, the memory is freed. It’s just that it doesn’t always need the reference counting part. But you can rest assured that it will be freed. With manual memory management, the programmer must manually free the object, and there is no automatic method to do this once there are no references. That is what makes it manual. The programmer might forget to free the memory or they might free memory too soon and try to use an object after it’s already been freed. This is not the case in Rust.
- Calavar 4y ago> And this is basically what Rust does with ownership. When the object goes out of scope and ownership is not transferred, the memory is freed. It’s just that it doesn’t always need the reference counting part. But you can rest assured that it will be freed. That's manual memory management. Like I said, the fact that you didn't explicitly write the call to free doesn’t make it automatic - that's just syntax sugar. Explicitly transferring ownership involves the programmer manually telling the compiler "keep this alive." If you're the one doing the bookkeeping, that's manual. Rust helps you with the bookkeeping, but it doesn't eliminate altogether. As opposed to a tracing GC, where you don't need decide on the scope of the value itself (just the scope of the reference), leave lifetime annotations, use std::move or the like to transfer explixitly ownership, or write out destructor/drop procedures to tell the compiler exactly how to free memory. The runtime does all the bookkeeping on its own. > The programmer might forget to free the memory or free memory too soon and use an object after it’s already been freed. This is not the case in Rust. Yes, it's possible to run into pathologic cases in any algorithm for automatic memory management where memory isn't freed - that's the nature of Turing completeness. No scheme for automatic memory management claims to be 100% foolproof. That doesn’t mean that tracing GC is actually manual memory management. > This just isn’t true, and it would be anarchy. You know that the object will be freed sometime after the last reference to it dies. This is the whole reason to use automatic memory management. That's a surprisingly literal interpretation of what I said. What I mean by "you have no idea" is that if you read a function that uses reference counting or tracing GC, you cannot say for sure if there are going to be deallocation when that function is called, even for a pure function where you know the particular input values to that function. That's because the decision to deallocate depends on the whole program state, including references to the same value that may be held by some third party library that you linked in. As opposed to Rust, where (unless you are using Rc or Arc) you could annotate that function with comments about where things will be deallocated and if you understand the semantics of the language you will be right every time. Not because you are a genius who can divine the machinatioms of the compiler/optimizer running through algorithms to automatically insert frees, but because you actually made those decisions yourself, whether implicitly or explicitly, by leveraging the semantics of the language. Like I said, this is not a new concept. You could write C++ code without any instance of new, delete, malloc, or free all the way back in the 80s.