Seems like you've mostly got the uncertainties figured out, so I'll only address what you haven't addressed by yourself already.
but my user ids are not sequencial.
That changes things a bit... but then you don't have access to "handy accessors to quickly find the name of the user with id 99" in the map version either, do you? At most, you can make the user ID the key, which still offers lower performance than an array. If speed is a concern, arrays are faster than maps, which are faster than structs as of writing.
If memory usage is not a concern and you're not planning to have a billion of these data sets, one approach would be to use an array and just leaving indices empty if there's no data to fill them with. That would deliver the best performance, and it's also the least round-about way of storing data that's indexed by a number.
In general, there's a trade-off between performance, memory usage and ease of use when determining whether to use arrays, maps or structs. Which of these should be prioritized highly depends on the circumstances of the project and the feature these are to be used in.
Maybe my english is bad. For me, passing an ID, is like passing a pointer, so it is "by reference" : you do not duplicate the data : you work on the same data, no ?
Anyway, looking at the debugger array IDs, it seems that, as you said, the arrays keep the same ID when they are passed inside functions (like the function below).
"are passed by reference and copied on write" : I do not get this one... If you pass them by reference(pointers) then if you modify the reference, you modify the original data, no ? So what is the need of this @ accessor ? I am used to C++ and the byref / by val difference induced by the "&" symbol, and I suspect that GMS works differently and that's why I'm confused...
If it is "like" something, it "is" not something.
But you are correct in that array references are not pointers (as confirmed by
is_ptr).
Whether you can modify the original data using the passed value as a reference does not make something "pass by reference". Different languages have different definitions of what they call "pass by reference", but the general idea is that if something is passed by reference, modifying the argument - as in
directly modifying the argument, not using it to index something, as is the case with map IDs - modifies the original. If something is passed by value, modifying the passed argument does
not modify the original.
GML arrays are somewhat of a borderline case here, as you can
either modify the original or not modify the original depending on whether you use the array accessor or not when writing to an array.
For example, if you pass an array to a function parameter
arr:
arr[0] = "copy"; inside of that function creates a
copy of the array you passed in, assigns the reference to this copy to
arr and then sets the 0th cell of the copy (and only the copy - not the original) to "copy". The original still contains whatever it contained before it was passed to the function. Two arrays exist now - the original and the copy (which will cease to exist at the end of the function call).
arr[@ 0] = "original";, on the other hand, modifies the original array that you passed into the function.
arr still holds a reference to the same array as the one you passed in. Only one array exists here, and there are two variables that hold a reference to it (the original outside of the function and
arr in the function).
Most of the official documentation claims that arrays are "
passed by reference" regardless of this, hence the terminology I use when describing this as "
pass by reference and copy on write" even though it's more along the lines of "passed
as a reference".