In my proposal about "real arrays" I gave the following proposal:
type struct
{
pointer_size length;
some_type *start;
}
It might be better putting the pointer first, since that way we can convert to the pointer with a simple cast:
type struct
{
some_type *start;
pointer_size length;
}
I wrote that "All arrays are always passed by reference", meaning that the struct is actually passed by value, but since it is a fat pointer, it actually becomes pass by ref. So this makes sense for fixed arrays, but for dynamic arrays that breaks down.
Our dynamic array should look like this as a struct:
type struct
{
some_type *start;
pointer_size length;
pointer_size allocated;
Allocator *allocator;
}
But for this we need to pass the whole array by value!
Unfortunately, we can't then make slices in the same manner as with the fixed vector, a slice to a dynamic vector needs to look different from a slice to a normal vector. This is rather vexing since we would have preferred to let them convert into each other. Since the first two fields are structurally the same for the fixed array and the dynamic one, they could transform into each other.
This is safe:
func void foo(int[*] y) { ... };
func void bar(int[] x) { ... };
int[*] a = { 1, 2, 3 };
foo(a);
int[] b = cast<int[]>(a);
bar(a); // converts to foo(cast<struct FixedArray>(a))
printf("%d", b[0]);
These are unsafe:
func void foo(int[*] y); { ... }
func void bar(int[] x) { ... };
int[*] a = { 1, 2, 3 };
int[] b = cast<int[]>(a);
int[] c = a[0:1];
foo(a);
printf("%d", b[0]); // The pointer in b might have been freed.
printf("%d", c[0]); // The pointer in c might have been freed.
Either we simply accept these weaknesses... we see the errors as similar to the exceptions that occur in say, Java when getting a view of a map or a list that's later updated. OR we try to be clever. I'm somewhat for the less clever solution that might lead to errors when used incorrectly.