there are serious downsides to this, its a tradeoff and I wouldn't say they are "better pointers".
First, you're obviously paying indirection tax on EVERY lookup. In addition, you're also paying (somewhat trivially) the generation counter tax (assuming you want any kind of safety).
Next, you have the complexity, hopefully you are making type-safe versions of all these indices so you can't just use an aribtrary index into any kind of array right? Ok well that means now you have double the types (one index type per pointer type). Ok maybe not the worst, just a lot of boilerplate everywhere.
Next, you have the issue of resizing your memory array. You now are in "write your own garbage collector" land. You want to consolidate 50% of the memory that is free? Well you need to move every index to the new compacted range (expsensive), or manage a free list (potentially double the memory requirement, and way more complexity, cache coherence issues, etc). Do your memory lists grow indefinitely? Do you need to think about having separate lists for short lived and long lived (generational GC style)?
Then there's the issue of object pools. You want your data to live in arrays. Do you put all objects into one master array? You lose cache coherence / iteration speed, and type safety (unless you really bloat and start adding type info to every entry). If you use different arrays for each you might end up with dozens of arrays, one per type, and also their free lists. Now you're in "collection pointer hell".
This article reminds me (on the surface, this is deeper than my experience) of how the recent Goose language orients things.
I never worked with it but I seem to recall the old original Mac API's (Pascal based) used "handles" a lot to solve the issue of being able for the system to move allocations and memory out from under a program without it knowing, as a "pointer to pointer to <thing>". I was poring over those manuals in ~1988/1989 with visions of being a full-time Mac developer in my head.
I remember back in the day in Windows C++ programming everything was a HANDLE memory is probably not serving me, but they were all really just void* renamed. And you use them with various win32 functions I guess to keep things straight. I don't miss those days.
The good thing is that all of these can and have been quite neatly put into RAII[1] or some memory arena with custom deleters which use the Win32 functions. One could manually `delete` those handles, but there be dragons. Vulkan does something similar; everything is a VkSomething handle. They may just be pointers directly exposed by the driver, but even so, users are explicitly supposed to use vkDeleteSomething functions to delete them.
and why game engines use entity-component systems, analytical databases are column-oriented, etc. That is, the classical organization of "objects" as structures has a lot of overhead when you're doing heavy work. For instance java reifies objects with a big header, a monitor lock, and other stuff that really adds up if you, say, are writing a chess engine and need to maintain transposition tables. And even if you are in C++ and there is a lot less overhead, AoS still has bad mechanical sympathy compared to SoA.
there are serious downsides to this, its a tradeoff and I wouldn't say they are "better pointers".
First, you're obviously paying indirection tax on EVERY lookup. In addition, you're also paying (somewhat trivially) the generation counter tax (assuming you want any kind of safety).
Next, you have the complexity, hopefully you are making type-safe versions of all these indices so you can't just use an aribtrary index into any kind of array right? Ok well that means now you have double the types (one index type per pointer type). Ok maybe not the worst, just a lot of boilerplate everywhere.
Next, you have the issue of resizing your memory array. You now are in "write your own garbage collector" land. You want to consolidate 50% of the memory that is free? Well you need to move every index to the new compacted range (expsensive), or manage a free list (potentially double the memory requirement, and way more complexity, cache coherence issues, etc). Do your memory lists grow indefinitely? Do you need to think about having separate lists for short lived and long lived (generational GC style)?
Then there's the issue of object pools. You want your data to live in arrays. Do you put all objects into one master array? You lose cache coherence / iteration speed, and type safety (unless you really bloat and start adding type info to every entry). If you use different arrays for each you might end up with dozens of arrays, one per type, and also their free lists. Now you're in "collection pointer hell".
Of course, you could just wisely use pointers...
This article reminds me (on the surface, this is deeper than my experience) of how the recent Goose language orients things.
I never worked with it but I seem to recall the old original Mac API's (Pascal based) used "handles" a lot to solve the issue of being able for the system to move allocations and memory out from under a program without it knowing, as a "pointer to pointer to <thing>". I was poring over those manuals in ~1988/1989 with visions of being a full-time Mac developer in my head.
I remember back in the day in Windows C++ programming everything was a HANDLE memory is probably not serving me, but they were all really just void* renamed. And you use them with various win32 functions I guess to keep things straight. I don't miss those days.
> but they were all really just void* renamed
The good thing is that all of these can and have been quite neatly put into RAII[1] or some memory arena with custom deleters which use the Win32 functions. One could manually `delete` those handles, but there be dragons. Vulkan does something similar; everything is a VkSomething handle. They may just be pointers directly exposed by the driver, but even so, users are explicitly supposed to use vkDeleteSomething functions to delete them.
[1]: https://github.com/microsoft/wil
Famously Win16 API required locking memory allocations to be able to access them by a pointer. Unlocked allocations could be moved by the OS.
my API always use handels, not because i design them that way, but in every iteration they get more and more handles
A nice analysis of large-scale programming techniques that go beyond the novice version of "OO Sux" to the almost secret
https://en.wikipedia.org/wiki/AoS_and_SoA
and why game engines use entity-component systems, analytical databases are column-oriented, etc. That is, the classical organization of "objects" as structures has a lot of overhead when you're doing heavy work. For instance java reifies objects with a big header, a monitor lock, and other stuff that really adds up if you, say, are writing a chess engine and need to maintain transposition tables. And even if you are in C++ and there is a lot less overhead, AoS still has bad mechanical sympathy compared to SoA.
see Jovial language (https://news.ycombinator.com/item?id=27390796) SERIAL and PARALLEL tables.