[БЕЗ_ЗВУКА] В это видео мы проговорим уже про более сложный тип данных. И начнем мы с массива. Массив — это какой-то набор из нескольких данных, одного типа. Особенностью массивов в Go является то, что размер массива — это часть типа данных. Что это значит? Это значит, что массив размерностью 2 и массив размерностью 3 — это два совершенно разных типа данных и они между собой несовместимы. Причем размерность массива задается при компиляции, она не может быть изменена динамически. Как объявляется массив? Массив объявляется, используя квадратные скобки, в которых указан его размер, и потом тип данных, который находится внутри этого массива. Вот в данном случае я создам массив из трех элементов, под них сразу же будет выделена память, и эти элементы сразу же будут проинициализированы значениями по умолчанию. Для int'а это 0. Я могу использовать константы для определения размера массива, но не переменные. То есть написать вот так уже нельзя, компилятор будет ругаться. Еще можно определить массив при инициализации. То есть я могу указать три точки, это значит, что возьми столько элементов, сколько я тебе скажу, в фигурных скобках. И в фигурных скобках могу сразу же инициализировать этот массив. В данном случае у меня тут будет три элемента. Если же я попробую обратиться к массиву, выходя за его пределы, то это будет проверено при инициализации. Если это константа, и код просто не скомпилируется. Если же там будет такой id индекс, то это будет проверено уже в run time, и программа завершится паникой. Итак, поскольку мы не всегда знаем, какого размера точно нам нужен массив и мы никак не можем это определить в run time, то массив — это довольно низкоуровневый тип данных, он используется не очень часто. Гораздо чаще используется другой тип, который основывается на массиве и называется он слайс. Итак, слайсы. Слайс — это чуть-чуть более сложная структура данных, чем массив, потому что у слайса есть его длина, то есть то количество элементов, которое там уже есть. И capacity, то есть то количество элементов, которое влезет еще в этот слайс без аллоцирования дополнительной памяти. Для начала рассмотрим просто создание слайса. Слайс можно создать, просто не указывая размерность в квадратных скобках. Очень похоже на массив. Такое писать вы будете очень часто. В данном случае на этой строчке я создам совершенно пустой слайс, у него не будет ни длины, ни capacity, он не инициализирован, там nil внутри. Следующим образом я создаю пустой слайс, который уже инициализирован, но у которого значения по-прежнему нет. Длина его 0, и capacity 0. Могу инициализировать чем-то сразу. В данном случае я кладу туда 42, и под это сразу выделяется массив размерностью 1 элемент, то есть его длина 1 и capacity тоже 1. Но гораздо чаще используется встроенная функция make, которая создает массив нужной размерности и capacity. Например, make слайс int'ов 0 создает мне вообще совсем пустой слайс. То есть у него нет ни значений, ни какой-нибудь памяти под него не инициализировано. Однако если я укажу тут 5 вторым параметром, то я уже создам слайс, который будет проинициализирован пятью элементами, которые конечно же, будут иметь значения по умолчанию. При этом capacity будет являться также 5. И я могу указать сразу capacity, то есть сразу размерность нижележащего массива, уже на слайс, на который ссылается этот слайс этой области памяти. Это очень полезно, если вдруг вы знаете, сколько элементов у вас будет в этом слайсе. Например, вы создаете слайс из нуля элементов, но вы сразу аллоцируете память для десяти элементов. Это очень положительно сказывается на быстродействии программы, потому что если вы доходите до конца, если вы упираетесь в capacity, то тогда ваша программа выделит новую область памяти, вдвое большего размера, и скопирует туда все значения из одной области в другую. А старые значения уберется уже сборщиком мусора. Итак, обращаться к элементам слайса можно также через квадратные скобки, как к массивам. При этом если вдруг вы обратитесь к элементу, который выходит за границы этого слайса, то выкинетеся run time паника. Итак, добавление элементов. Для того чтобы добавить элемент в слайс, есть специальная функция append, первым параметром которой передается сам слайс, а далее идут значения, которые вы хотите добавить. Вот в данном случае у нас сначала там нет ничего, после в массив в слайс добавлено два элемента — 9 и 10. И теперь я на следующей строчке добавляю туда еще один элемент, и уже capacity у меня становится не 3, а 4, потому что при увеличении размерности run time просто делает x2 от предыдущего размера. Если у вас есть слайс, вы хотите его домержить в текущий слайс, то для этого есть специальный оператор троеточие, Дело в том, что если бы мы написали вот так, то получилось, что в первоначальный слайс мы бы захотели добавить уже не int'ы, а другой слайс. Это получаются несовместимые типы данных. Поэтому для того чтобы добавить туда элементы другого слайса, используется троеточие. Ну и какие-то данные о слайсе вы можете получить, используя встроенную функцию len, которая говорит вам длину массива, то есть сколько там уже элементов, и функцию cap, capacity, которая говорит, сколько памяти аллоцировано. То есть len — это, условно говоря, вот это, а capacity — это может быть вот столько, то есть сколько всего туда может влезть, до того как будет аллоцирована еще память, до того как слайс будет расширен. Еще одной особенностью слайса является то, что вы можете взять какой-то кусок его, который будет ссылаться ровно на ту же область памяти, на которую ссылается оригинальный слайс. Вот в данном примере у меня объявлен слайс из пяти элементов от 1 до 5. Для того чтобы взять уже именно... Это как раз и называется слайс, получение среза. Для того чтобы получить элементы, сослаться на какую-то его часть, вы можете в квадратных скобках указать, с какого по какой элемент не включительно вы хотите сослаться. В данном случае я ссылаюсь на середину, при этом можно пропустить либо первое значение и сослаться от нуля до в данном случае двойки, либо же пропустить второе значение и сказать: вот отсюда и до конца. В этом есть очень важная особенность при работе со слайсами, которую нужно понимать. Если вы ссылаетесь на ту же область памяти, вот в данном случае я создал новый слайс такой же размерности, указав «дай мне все», и я изменю там какое-либо значение, то изменится в обоих слайсах. Однако если я добавлю значение вот в этой новый слайс, то тогда произойдет увеличение размерности этого слайса, потому что я создал слайс, у которого capacity 5, и в нем лежит 5 элементов. Если я добавлю туда 6-й элемент, то слайс должен будет расшириться, а что это значит? Это значит, что создастся другая область памяти, и туда скопируются значения, то есть он уже будет ссылаться на другую область памяти. И теперь если я поменяю какое-то значение, то оно то оно изменится уже в новом слайсе, а в старом не изменится. Это очень важная особенность, поиграйтесь с примером, посмотрите, как оно работает. Теперь еще момент копирования слайса. Иногда бывает нужно скопировать один слайс в другой: не создать именно ссылку на ту же область памяти, а именно честно скопировать. Вот такой способ неправильный, как я показываю, когда вы просто создаете вообще пустой слайс и вызываете функцию копии. Да, копии, конечно же, копируют элементы. Почему? Потому что она копирует в уже существующий, уже в занятые элементы, то есть то, что показывает нам переменная len. В данном случае len для этого слайса равна 0, и мы ничего не скопируем. Для того чтобы скопировать полноценно, нам нужно создать новый слайс, такой же размерности и такой же длины, сразу [НЕРАЗБОРЧИВО] с данными, и уже скопировать в него. Тогда это будет правильный вариант, вы получите ожидаемое поведение. Еще копии может копировать — это не обязательно должна быть переменная, это может быть срез, слайс, который вы только что получили, который ссылается на какую-то другую область памяти, то есть на другой слайс, на часть другого слайса. Это очень полезный трюк, когда вам нужно сначала записать длину данных, а потом сами данные, при этом сами данные — вы еще их размер не знаете, например, там какая-то бинарная упаковка. Поэтому вы пишете сначала нулевой размер, потом пишете данные, а потом, используя вот такой трюк, вы пишете в это место, в это место вы уже пишете длину. Слайсы очень широко используются в Go. Вы практически никогда не будете использовать массивы, и практически всегда вы будете использовать слайсы. Еще одним типом данных, про которые мы поговорим — это map. map, он же хеш-таблица, он же ассоциативный массив, он же сложное слово «отображение». map позволяет по ключу быстро получить значение. Это очень удобно, когда у вас значений довольно много. И если бы они лежали в слайсе, вам бы пришлось все перебирать, а так вы сразу идете в нужное место. Как определяется map? При помощи ключевого слова map, потом в квадратных скобках идет тип ключа и тип данных. При этом вы можете, конечно же, сделать вот так, то есть мапа, мап, мапа — мапов. Что может выступать в ключе, в качестве ключа. В качестве ключа может выступать любая сравниваемая структура данных. Итак, объявление. В данном случае я объявляю мапу сразу же при инициализации, вот так. И если я скажу «там будет nil», то есть нужно инициализировать. Вот так я инициализирую. Я указываю ключ, через двоеточие я указываю значение. И запятая, чтобы сказать компилятору, что там что-то идет дальше. При этом мапу можно точно так же создать, выделив сразу нужно место под то количество элементов, которое я там хочу, чтобы тоже не приходилось расширять ее в рантайме. Через len я могу получить количество элементов в мапе. Через обращение к элементу в квадратных скобках я обращаюсь уже к тем данным, которые там лежат. Но есть одна особенность: если там этого элемента нету по этому ключу, то вам вернется значение по умолчанию. Про это надо знать, потому что если вдруг у вас мапа, которая состоит из булов, то значение по умолчанию для була — это false. И вам вернется false, то есть вы должны как-то отличать что вам вернулось: это значение false вам вернулось, оно там реально лежит, либо его там нет. Для разрешения этой ситуации можно получить признак существования ключа. Он может быть получен, используя вторую переменную при обращении к мапе. В данном случае я получаю признак существования ключа в переменную nNameExist. То есть и в нем уже будет лежать булевая переменная, которая будет говорить, был там ключ либо не было там ключа. Вы будете очень часто это использовать, запомните это. Если же мне нужно только проверить, есть там значение или нет, то я могу пропустить первую переменную, используя символ подчеркивания — пустую переменную, говоря: да, я знаю, что там должна быть переменная, но она мне не нужна, не надо ее никуда присваивать. И, наконец, удаление ключа. Удаление ключа происходит через встроенную функцию delete. delete, указываете мапу и указываете ключ — все просто. На этом со сложными типами данных мы закончили, теперь давайте поговорим про структуры.