Найти в Дзене
Simple Prog

Append в Go слайсах. Как не попасться

Слайсы в Go - очень удобный способ работы с динамическими массивами. Однако они не всегда ведут себя так, как можно было бы ожидать. Я полагаю, что если вы читаете это, то вы уже каким-то образом знакомы с Go, поэтому я не буду подробно описывать его и сосредоточусь только на срезах (слайсах). Срезы в Go - это очень полезная абстракция массивов Go. И срезы, и массивы имеют типизированное значение, но массивы также имеют определенную статическую длину, поэтому они обычно не так полезны, поскольку вы не можете добавлять к ним что-либо, вырезать их и вообще манипулировать ими удобным способом. То, чего не хватает массивам (по замыслу), обеспечивается срезами. Одним из наиболее часто используемых методов является встроенная функция добавления: В качестве первого аргумента он принимает фрагмент, к которому мы хотим добавить, а в качестве второго элемента, к которому мы хотим быть добавленными. Он не изменяет фрагмент, указанный в качестве первого аргумента. Вместо этого он возвращает новый
Оглавление

Слайсы в Go - очень удобный способ работы с динамическими массивами. Однако они не всегда ведут себя так, как можно было бы ожидать.

Я полагаю, что если вы читаете это, то вы уже каким-то образом знакомы с Go, поэтому я не буду подробно описывать его и сосредоточусь только на срезах (слайсах).

Срезы в Go - это очень полезная абстракция массивов Go. И срезы, и массивы имеют типизированное значение, но массивы также имеют определенную статическую длину, поэтому они обычно не так полезны, поскольку вы не можете добавлять к ним что-либо, вырезать их и вообще манипулировать ими удобным способом. То, чего не хватает массивам (по замыслу), обеспечивается срезами.

Одним из наиболее часто используемых методов является встроенная функция добавления:

-2

В качестве первого аргумента он принимает фрагмент, к которому мы хотим добавить, а в качестве второго элемента, к которому мы хотим быть добавленными. Он не изменяет фрагмент, указанный в качестве первого аргумента. Вместо этого он возвращает новый фрагмент.

Выглядит просто, не так ли?

Но есть одна загвоздка:

-3

Вы можете запустить этот пример здесь https://play.golang.org/p/G3mmXSNEdY и увидеть, что sliceFromLoop всегда выводит последнее добавленное к нему значение!

Почему все предыдущие новые фрагменты, возвращаемые функцией append, изменяются при последнем добавлении?

Потому что Go механика append постоянно манипулирует базовым массивом, и все новые фрагменты и их значения также основаны на этом массиве.

Это означает, что создание новых переменных фрагмента на основе append может привести к тому, что ошибки и неполадки будет очень сложно обнаружить.

Каково решение?

  • используйте append только для добавления нового значения к заданному фрагменту, а не для создания нового:
-4
  • если вы хотите создать новый фрагмент на основе старого с каким-либо добавленным значением, всегда сначала копируйте его:
-5

или просто создать неглубокую копию старого фрагмента:

-6

Это не очень часто используемый шаблон. Я обнаружил эту проблему после нескольких лет написания производственного кода на Go, и, вероятно, именно поэтому она еще больше сбивает с толку и расстраивает.

Больше информации о слайсах:

https://blog.golang.org/slices
http://blog.golang.org/go-slices-usage-and-internals
очень полезный веб-сайт о подводных камнях Go, включая упомянутый выше

Если вам понравилась статья - напишите комментарий, это очень помогает продвижению!