It's quite possible to program imperatively with no GC, no borrow checker, and no memory leaks in Haskell or in other languages such as Ada. The main technique is to simply not allocate or free anything, but instead just use fixed buffers, or objects that you have allocated before entering the no-GC region. In Haskell and other GC'd languages, GC generally triggers on allocation, so no allocation = no GC.
Back when memory was scarce this was a well known practice in Lisp. You'd just adopt an ugly style in the parts of the program where performance was important, doing destructive updates instead of consing in order to avoid GC'ing in those parts. Rust's contribution (the borrow checker) is in letting you make more use of dynamic memory allocation while still not using a GC or programming unsafely.
Back when memory was scarce this was a well known practice in Lisp. You'd just adopt an ugly style in the parts of the program where performance was important, doing destructive updates instead of consing in order to avoid GC'ing in those parts. Rust's contribution (the borrow checker) is in letting you make more use of dynamic memory allocation while still not using a GC or programming unsafely.
You might look at this presentation for more: https://www.st.cs.uni-saarland.de/edu/seminare/2005/advanced...