Slices and Dynamic Arrays

Jonathan M Davis newsgroup.d at jmdavisprog.com
Tue Jan 2 19:17:05 UTC 2018


On Tuesday, January 02, 2018 10:37:17 Ali Çehreli via Digitalmars-d-learn 
wrote:
> As soon as we call it "dynamic array", I can't help but think "adding
> elements". Since GC is in the picture when that happens, it's essential
> to think GC when adding an element is involved.
>
> Further, evident from your description it's a "slice" until you add
> elements because the underlying memory e.g. can be a stack-allocated
> fixed-length array.
>
> For these reasons, the interface that the program is using is a "slice".
> Dynamic array is a different concept owned and implemented by the GC.

Except that from the standpoint of the API, T[] _is_ the dynamic array -
just like std::vector is the dynamic array and not whatever its guts are -
and the semantics are the same whether it's backed by the GC or by a static
array or by malloc-ed memory or whatever. Appending works exactly the same.
Reallocation works the same. None of that changes based on whether the
dynamic array is backed by GC-allocated memory or not. It's just that the
capacity is guaranteed to be 0 if it isn't GC-allocated and so the first
append operation is guaranteed to reallocate. The semantics of T[] itself
don't change regardless, and most code doesn't need to care one whit about
what kind of memory backs the dynamic array. No matter what memory backed it
to start with, you get the same appending semantics. You get the same
semantics when accessing the data. You get the same semantics when passing
the dynamic array around. None of that depends on what kind of memory the
dynamic array is a slice of. T[] functions as a dynamic array regardless of
what memory backed it to start with, and as such, I completely agree with
the spec calling it the dynamic array.

And as soon as you start talking about T[] not being a dynamic array, you
get this weird situation where T[] has all of the operations and semantics
of a dynamic array, but you're not calling it a dynamic array simply because
it happens to be a slice of memory that wasn't GC-allocated. So, you have
this type in the type system whose semantics don't care what memory
currently backs it and where code will act on it identically whether it's
GC-backed or not, but folks want to then act like it's something different
and treat it differently just because it happens to not be GC-backed at the
moment - and the same function could be called with both GC-backed and
non-GC-backed dynamic arrays. The type and its semantics are the same
regardless.

Of course, understanding how and when reallocation occurs matters if you
want to understand the exact semantics of copying a dynamic array around or
when appending or reserve is going to result in a reallocation, but that
doesn't necessitate calling the GC-managed buffer the dynamic array. It just
requires understanding how it's the GC that manages capacity, reserve, and
appending rather than the dynamic array itself. But the API is that of a
dynamic array regardless. If it weren't, you couldn't append to T[] any more
than you can append to an arbitrary range. As soon as you insist on calling
them slices, you're basically talking about them as if they were simply
ranges rather that than the container/range hybrid that they are.

Regardless, the fact that they're a container/range hybrid is what makes
this such a mess to understand. The semantics actually work fantastically if
you understand them, but it sure makes understanding them annoyingly
difficult.

- Jonathan M Davis




More information about the Digitalmars-d-learn mailing list