Le slice sono oggetti di tipo T[]
per qualsiasi tipo T
.
Rappresentano una vista su un sottoinsieme di un array di valori T
, oppure sull'intero array.
In D, le slice e gli array dinamici sono la stessa cosa.
Una slice è composta da due elementi:
T* ptr; size_t length; // unsigned 32 bit su sistemi a 32bit, unsigned 64 bit su sistemi a 64bit
Quando si crea un nuovo array dinamico, viene restituita una slice che punta alla memoria appena allocata:
auto arr = new int[5];
assert(arr.length == 5); // la memoria è referenziata in arr.ptr
In questo caso, la memoria allocata è gestita automaticamente dal garbage collector. La slice funge da "vista" sugli elementi sottostanti.
È possibile ottenere una slice che punta a memoria già esistente utilizzando l'operatore di slicing. Questo operatore può essere applicato a:
opSlice
Nell'espressione origin[start .. end]
, l'operatore di slicing crea una vista di tutti gli elementi di origin
da start
fino all'elemento precedente a end
:
auto newArr = arr[1 .. 4]; // l'indice 4 NON è incluso
assert(newArr.length == 3);
newArr[0] = 10; // modifica newArr[0] che corrisponde ad arr[1]
Le slice creano una nuova vista sulla memoria esistente, non una copia dei dati. La memoria verrà liberata dal garbage collector solo quando nessuna slice mantiene più riferimenti ad essa o a una sua parte.
Grazie alle slice, è possibile scrivere codice molto efficiente per operazioni che lavorano su blocchi di memoria (come i parser), accedendo solo alle porzioni di interesse senza necessità di nuove allocazioni.
Come visto nella sezione precedente, [$]
è una sintassi abbreviata per arr.length
. Quindi arr[$]
tenta di accedere all'elemento oltre la fine della slice, generando un RangeError
(se i controlli dei limiti sono attivi).