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