Когда жизнь дает тебе лимоны
Головоломка
Как вы думаете, что напечатает следующая программа?
Эта программа напечатает:
cart: [apple pear lemon] fruits: [apple pear lemon]
Добавление к fruits
затронуло и cart
. Этот результат связан с тем, как в Go построены слайсы.
Если вы посмотрите на slice.go
, вы увидите следующее определение:
array
это указатель на базовый массив данных, на который смотрит срезlen
- это количество элементов, на которые смотрит срез.cap
— это количество элементов отarray
до конца базового массива.
В нашем случае, прежде чем мы добавим к fruits
, данные выглядят так:
И cart
, и fruits
указывают на один и тот же базовый массив. Когда вы добавляете к fruits
, Go будет искать, есть ли больше места в базовом массиве; если есть — он будет использовать это пространство. В противном случае Go выделит новый массив большего размера. Скопируйте данные, а затем добавьте.
Вот пример реализации append
для целых чисел:
В нашем случае fruits
имеет еще одно место в базовом массиве. append
будет использовать тот же базовый массив, на который смотрит cart
.
Если вы напечатаете len
и cap
перед добавлением:
Ты увидишь:
Как это исправить? Вы можете использовать выражение полного среза:
И теперь вы получите:
cart: [apple pear milk] fruits: [apple pear lemon]
Теперь, когда вы печатаете len
и cap
перед добавлением:
Ты увидишь:
Так как емкость fruits
равна 2
, Go выделит и скопирует. Теперь поле array
cart
и fruits
указывает на разные массивы.
Срезы могут показаться простыми, но у них есть некоторые очень сложные пограничные случаи, о которых вам нужно знать. Хорошая новость заключается в том, что в большинстве случаев срезы будут вести себя именно так, как вы думаете.
Если вам нравится решать задачи по программированию, ознакомьтесь с книгами Мики Тебека «Дразнилки для ума» на The Pragmatic Bookshelf. Вы можете сэкономить 35% на электронных версиях книг с промокодом brain_teasers_35 до 31 октября 2022 года: