You can have fairly cheap (but not entirely free) runtime memory safety at interface boundaries in any language that supports arrays via "tagged index handles" (basically weak references which protect against dangling access). To be efficient this requires a specific module design philosophy though (you basically want to avoid converting between a handle and a pointer for each memory access, only at the interface boundary, and interfaces should be designed so that they avoid "high frequency functions" with handle parameters).
Interestingly this approach is also somewhat popular in Rust to workaround borrow checker restrictions.
Interestingly this approach is also somewhat popular in Rust to workaround borrow checker restrictions.
For instance see:
https://floooh.github.io/2018/06/17/handles-vs-pointers.html