Объясните, как работает встроенная функция `copy` в Go. Каковы её основные особенности и в каких случаях она предпочтительнее ручного копирования в цикле?

Ответ

Встроенная функция copy предназначена для копирования элементов из одного слайса (source) в другой (destination). Она возвращает количество скопированных элементов.

Ключевое правило: copy скопирует min(len(src), len(dst)) элементов. То есть, количество скопированных элементов будет равно наименьшей из длин исходного и целевого слайсов.

Пример:

src := []int{1, 2, 3, 4} // Источник
dst := make([]int, 2)   // Целевой слайс с длиной 2

numCopied := copy(dst, src)

fmt.Println(dst)       // Вывод: [1 2]
fmt.Println(numCopied) // Вывод: 2 (скопировано 2 элемента, т.к. len(dst) == 2)

Основные особенности и преимущества перед ручным копированием:

  1. Безопасность: copy корректно обрабатывает случаи, когда исходный и целевой слайсы пересекаются (указывают на одну и ту же базовую область памяти). Ручное копирование в цикле в такой ситуации может привести к неожиданным результатам.
  2. Производительность: Как встроенная функция, copy реализована на низком уровне и оптимизирована. Она, как правило, работает значительно быстрее, чем копирование элементов в цикле for на Go.
  3. Лаконичность: Использование copy делает код более чистым и читаемым.

Важно помнить: Для copy необходимо, чтобы у целевого слайса dst была достаточная длина (не емкость!), чтобы принять элементы. Если вы передадите nil или слайс с нулевой длиной в качестве dst, ничего скопировано не будет.

Ответ 18+ 🔞

Ага, слушай, вот есть у тебя два слайса, как два брата-алкаша: один с бухлом (это source), а второй — пустой (это destination). И ты хочешь перелить из полной бутылки в пустую. Ну, функция copy — это как раз такая трезвая рука, которая делает это за тебя. Возвращает она, сколько стопок, блядь, перелила — число скопированных элементов.

Главный закон, ёпта: copy скопирует min(len(src), len(dst)) элементов. То есть, она посмотрит — у кого меньше, у источника или у приёмника, — и столько и перельёт. Не больше, сука, чем может вместить второй.

Смотри, как это выглядит:

src := []int{1, 2, 3, 4} // Это наш полный штоф
dst := make([]int, 2)   // А это — стопка, всего на две порции

numCopied := copy(dst, src) // Переливаем!

fmt.Println(dst)       // Смотрим: [1 2] — только две цифры, больше не влезло!
fmt.Println(numCopied) // А вот и счётчик: 2, потому что len(dst) был равен двум, блядь.

И чем же этот способ лучше, чем тупо в цикле копировать?

  1. Не обосрёшься с памятью: copy умная, она понимает, если слайсы ссылаются на одно и то же место в памяти (пересекаются). Если бы ты делал это вручную, циклом, мог бы получить такую кашу, что потом неделю разгребать. А тут — раз, и готово, без сюрпризов.
  2. Быстро, блядь, как угорелый: Это встроенная функция, она на низком уровне работает, оптимизирована под железо. Твой цикл на Go против неё — как черепаха против ракеты. Серьёзно, овердохуища быстрее.
  3. Коротко и ясно: Одна строчка вместо трёх-четырёх с циклом. Красота, а не код.

Но запомни, мудя: Для copy важно, чтобы у целевого слайса dst была длина (length), а не просто ёмкость (capacity)! Если ты передашь ему nil или слайс с длиной ноль — это как поднести дырявый стакан. Ни хуя не нальётся, ноль элементов и скопируется.