* It's still memory safe (in the traditional sense). No chance of overwriting return addresses or whatever.
* It's type safe. There's no risk of type confusion. There's an excellent crate called `typed_index_collections` that lets you use a unique index type for each vector so the index is properly typed (i.e. you have `UserIndex` and `CommentIndex` or whatever and you can't mix them up).
* You don't need to do it for your entire program.
I have used this extensively in a profiler that worked on array oriented data structures. It worked great, especially because the data was immutable.
For mutable data it would probably work less well. I haven't tried that.
* You can choose to use handles smaller than a pointer.
* The NFA is cyclic, which makes memory management quite the chore. Even if you're using Rust's reference counted pointers. But if you use handles, the actual memory management becomes a lot simpler.
Did you measure any performance improvements due to using small handles?
I have a medium-sized project that uses a ton of `usize`s to reference elements in `Vec`s and would love to know if there's some potential for performance improvements there.
But in the actual regex crate, I do indeed use `u32` as a representation for a handle. To a first approximation, it corresponds to about half as much memory usage.
Same deal with the aho-corasick crate.
We have a real program in front of us. Let's try to be concrete here please.
And in garbage collected languages you’d do the same to avoid the cost of GC.
There are plenty of reasons not to use C++, including the lack of memory safety, but if you've already decided to ditch that, C++ does let you pick and choose which parts you want to use.
Isn't that the case for most programming languages (ie, you can use the implementation you want/need), though?
The commitment to ensure that features do not add cost(memory, runtime) if you don't use them is something fairly uncommon in other languages, and many languages have features you can't meaningfully avoid.
If you have indices into an array you need access to that array, once the array is gone the indexes are “dead”.
If you have pointers into local memory without any sort of borrow checking once the local memory is gone you have UAFs.
Having it done for you automatically is certainly even better, but if you're first saddled with manual memory management, it's better than nothing.
But when you do, and your data is mutable, generational indices are a legit and very useful strategy that is used in C/C++ too.
See this: https://floooh.github.io/2018/06/17/handles-vs-pointers.html
But indices are still safer than raw pointers - vec access does bounds checks. You can make indices typed if you’re worried about mixing them up.