Думаю, многие из вас сталкивались с замысловатыми задачками, которые в реальной практике встретить почти невозможно, но которые очень любят давать во всяких тестах и на собеседованиях. В конце мая прошла конференция PHDays, на которой был тест как раз с такими задачками. К моему сожалению, я провалила этот тест, но затем разобралась что, как и почему, и хочу поделиться с вами. Итак, 5 картинок с кодом, к каждому дается 4 варианта ответа.  Варианты ответа: 1. [23 42]2. [23 42 84]  3. Программа не скомпилилируется  4. runtime error: slice out of range   Если попробовать скомпилировать код, то компилятор обратит ваше внимание на строку slice = append(slice, 84) и не просто так. Да, слайсы имеют ссылочный тип. Но если они ссылочные, что не так-то? Должно работать! А проблема в том, как работает функция append — она не меняет старый слайс, а возвращает новый. А из функции f мы результат не получаем. Соответственно, правильный ответ будет под номером 1: [23 42]. Чтобы получить ответ из пункта 2 ([23 42 84]), нам нужно этот результат как-то передать в main. Сделать это можно двумя способами. Во-первых, можно вернуть новый слайс из функции. Поправим немного код. (Go Playground) Во-вторых, можно вместо слайса передавать указатель на слайс: (Go Playground) Варианты ответа: 1. foofoo  2. foobar  3. compilation error: s redeclared in this block  4. compilation error: s undefined: s   Вы когда-нибудь видели, чтобы кто-то вот так использовал скобки? Я — нет. Однако давайте разберёмся, что тут происходит. Имеем две области видимости: внутри скобок и за их пределами. При этом с толку сбивают две одноимённых переменных. Такое называется variable shadowing и считается плохой практикой в Go. Существуют различные линтеры, которые выдают на подобный код предупреждение (например, этот линтер, который используется вместе с go vet, см. go help vet). Собственно, поэтому сначала программа напишет foo, а затем bar. Следовательно, правильный ответ будет под номером 2. Варианты ответа: 1. Числа от 1 до 99 по возрастанию  2. Числа от 1 до 99 в произвольном порядке  3. Вывод не определен  4. Ничего   Проблема состоит из двух частей. Во-первых, счётчик цикла создаётся лишь один раз и затем просто изменяется на каждой итерации. Во-вторых, при захвате переменных из окружения в замыкание (анонимную функцию) они захватываются по указателю. Т.о. захваченный счётчик цикла в каждой горутине один и тот же, и меняется во всех них разом. Поэтому вывод зависит от того, когда какая горутина успеет напечатать текст. Какой в этот момент будет счётчик, такой и напечатается. Большинство же горутин напечатает 100, т.к. такой цикл закончится гораздо быстрее, чем осуществится вывод. Следовательно, правильный ответ будет под номером 3 — вывод не определен. Чтобы получить цифры от 1 до 99 в произвольном порядке, есть два варианта. Во-первых, можно сделать копию счётчика цикла (i := i) перед созданием замыкания: (Go Playground) Во-вторых, можно сделать в анонимной функции параметр i int и передавать счётчик цикла в замыкание через этот параметр (тогда он будет копироваться при этой передаче): (Go Playground) Варианты ответа: 1. World  2. Hello  3. Hello World  4. compilation error: (func literal)() used as value   Чтобы понять, что происходит в этом коде, надо знать особенности defer. Эта конструкция выполнится только по завершению функции. Тут нас сбивает с толку Println, которы стоит после defer. Однако, если мы попробуем упросить код и убрать всё, что может нас запутать, то получим что-то такое: (Go Playground) Теперь правильный ответ очевиден, он под номером 2. Варианты ответа: 1. [a b c]  2. [a q c]  3. [b c]  4. output is undefined   На первый взгляд хочется спросить, а зачем тут вообще эта b? Мы же a печатаем. Вот теперь самое время вспомнить, что слайсы в Go — это ссылочный тип данных. Следовательно, на строке b := a[1:2], мы присваиваем в переменную b ссылку на область памяти из переменной a. А затем (b[0] = "q") редактируем эту самую область памяти. В итоге имеем в результате правильный ответ под номером 2. Если вы хотите именно скопировать слайс, то для этого придется использовать либо цикл, либо встроенную функцию copy. Программист