You do bring up a valid broader concern. Ironically, this is a reason that GC'd systems can sometimes be better for privacy than Ada or Rust which uses a lot more Vec+indexes. An index into a Vec<UserAccount> is riskier than a Java List<UserAccount>; a Java reference can never suddenly point to another user account like an index could.
But that aside, we're talking about memory safety, array-centric approaches in Zig and Rust can be appropriate for a lot of use cases.
There seems to be no practical difference here. Rust can do a reference to UserAccount, and Java can do an index into an ArrayList of UserAccounts. Or vice versa. As you wish.
The borrow checker, however, forces you to hold onto an index instead.
Programs often require inherent state with data that refers to other data. In these cases, one must circumvent the borrow checker, whether it be with indices, IDs, Rc, or whatever. The borrow checker simply does not allow changing data when someone has a reference to it (except for the rare case where we can use Cell).
It's a myth that we can rewrite any program to not circumvent the borrow checker.
There are dedicated data structures for this [1] that will not let you access another item by mistake.
One of my favorite alternatives is generational_arena [0] which also happens to be the library that inspired Vale's generational references!
[0] https://docs.rs/generational-arena/latest/generational_arena...
It doesn't, however, prevent you from accidentally scribbling over your own memory (buffer overflow, for example) or from scribbling over someone else's memory.
Imagine all variables in your program declared as static. This includes all buffers (with indexes instead of pointers), all nested structures, etc.