Срезы - это объекты типа T[]
, заданные для любого типа T
. Срезы обеспечивают
представление значений подмножества массива типа T
, либо просто указывает на
весь массив. Срезы и динамические массивы - это одно и то же.
Срез состоит из двух элементов - указателя на первый элемент и длину среза:
T* ptr;
size_t length; // беззнаковое 32/64 бита, в зависимости от архитектуры
При создании нового динамического массива, возвращается срез на эту свежевыделенную память:
auto arr = new int[5];
assert(arr.length == 5); // память, указываемая arr.ptr
В данном случае, реально выделенная память управляется сборщиком мусора, возвращённый срез выступает в качестве "вида" на нижележащие элементы.
Используя оператор среза также можно получить срез на некоторую уже
существующую память. Оператор среза может быть применён к другому срезу,
статическому массиву, структуре или классу, реализующему opSlice
и некоторым
другим сущностям.
Например, в выражении origin[start .. end]
, оператор среза используется для
взятия всех элементов origin
, начиная с элемента start
и заканчивая
элементом, идущем перед элементом end
:
auto newArr = arr[1 .. 4]; // индекс 4 НЕ входит
assert(newArr.length == 3);
newArr[0] = 10; // меняет newArr[0], он же arr[1]
Такие срезы создают новый вид на существующую память. Они не создают новую копию. Если ни один срез больше не содержит ссылок на эту память, или на её субсрез, память будет освобождена сборщиком мусора.
Используя срезы, можно писать очень эффективный код, например, для парсеров, которые оперируют одним блоком памяти и просто берут срезы частей, с которыми действительно нужно работать - нет необходимости в выделении новых блоков памяти.
Как было видно в предыдущем разделе, выражение [$]
является более короткой
записью arr.length
. Следовательно, arr[$]
указывает на следующий за
последним элемент и обращение к нему сгенерирует ошибку RangeError
(если
проверка границ массива не была отключена).