Slices are objects from type
T for any given type
Slices provide a view on a subset of an array
T values - or just point to the whole array.
Slices and dynamic arrays are the same.
A slice consists of two members - a pointer to the starting element and the length of the slice:
T* ptr; size_t length; // unsigned 32 bit on 32bit, unsigned 64 bit on 64bit
If a new dynamic array is created, a slice to this freshly allocated memory is returned:
auto arr = new int; assert(arr.length == 5); // memory referenced in arr.ptr
Actual allocated memory in this case is completely managed by garbage collector, returned slice acts as a "view" on underlying elements.
Using a slicing operator one can also get a slice pointing to some already
existing memory. Slicing operator can be applied to another slice, static
arrays, structs/classes implementing
opSlice and few other entities.
In an example expression
origin[start .. end] slicing operator is used to get
a slice of all elements of
start to the element before
auto newArr = arr[1 .. 4]; // index 4 ist NOT included assert(newArr.length == 3); newArr = 10; // changes newArr aka arr
Such slices generate a new view on existing memory. They don't create a new copy. If no slice holds a reference to that memory anymore - or a sliced part of it - it will be freed by the garbage collector.
Using slices, it's possible to write very efficient code for things (like parsers, for example) that only operate on one memory block, and slice only the parts they really need to work on. In this way, there's no need to allocate new memory blocks.
As seen in the previous section, the
[$] expression is a shorthand form for
arr[$] indexes the element one past the slice's end, and
thus would generate a
RangeError (if bounds-checking hasn't been disabled).