{"id":13640719,"url":"https://github.com/Konstantin8105/Effective_Go_RU","last_synced_at":"2025-04-20T06:34:24.013Z","repository":{"id":37431258,"uuid":"80044544","full_name":"Konstantin8105/Effective_Go_RU","owner":"Konstantin8105","description":"Перевод - Эффективный Go","archived":false,"fork":false,"pushed_at":"2024-10-05T10:35:40.000Z","size":401,"stargazers_count":808,"open_issues_count":4,"forks_count":124,"subscribers_count":45,"default_branch":"master","last_synced_at":"2025-04-06T21:09:13.675Z","etag":null,"topics":["documentation","effective-go","go","golang"],"latest_commit_sha":null,"homepage":"https://golang.org/doc/effective_go.html","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Konstantin8105.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-01-25T18:35:49.000Z","updated_at":"2025-04-05T12:35:50.000Z","dependencies_parsed_at":"2024-11-09T11:30:42.082Z","dependency_job_id":"c14c82f6-5d00-4668-93df-566256190395","html_url":"https://github.com/Konstantin8105/Effective_Go_RU","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Konstantin8105%2FEffective_Go_RU","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Konstantin8105%2FEffective_Go_RU/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Konstantin8105%2FEffective_Go_RU/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Konstantin8105%2FEffective_Go_RU/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Konstantin8105","download_url":"https://codeload.github.com/Konstantin8105/Effective_Go_RU/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249859688,"owners_count":21335988,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["documentation","effective-go","go","golang"],"created_at":"2024-08-02T01:01:13.783Z","updated_at":"2025-04-20T06:34:23.711Z","avatar_url":"https://github.com/Konstantin8105.png","language":"HTML","funding_links":[],"categories":["HTML","Содержание:"],"sub_categories":["GoLang"],"readme":"# Effective Go (RU) (Эффективный Go)\n\n------\n\n\u003e ⚠️ **Внимание!** \n\u003e Данный перевод не завершен и устарел, [смотри новый здесь в .md или .pdf формате](https://github.com/0x0FACED/effective-go-ru)\n\n\nОригинал смотри: https://golang.org/doc/effective_go.html\n`go version go1.7.4`\n\n------\n\n**Список дополнительных материалов:**\n* [Руководство сотрудничества](https://github.com/Konstantin8105/Contribution_Guide_RU)\n* [Эффективный Go (Свежий перевод)](https://github.com/0x0FACED/effective-go-ru)\n* [Эффективный Go](https://github.com/Konstantin8105/Effective_Go_RU)\n\n------\n# Оглавление\n\n- [Effective Go (RU) (Эффективный Go)](#effective-go-ru-эффективный-go)\n- [Оглавление](#оглавление)\n  - [Введение](#введение)\n    - [Примеры](#примеры)\n  - [Форматирование](#форматирование)\n      - [Абзац](#абзац)\n      - [Длина строки](#длина-строки)\n      - [Круглые скобки](#круглые-скобки)\n  - [Комментарии](#комментарии)\n  - [Именование](#именование)\n    - [Именование пакетов](#именование-пакетов)\n  - [Геттеры](#геттеры)\n    - [Имена интерфейсов](#имена-интерфейсов)\n    - [MixedCaps](#mixedcaps)\n    - [Точка с запятой](#точка-с-запятой)\n  - [Управляющие структуры](#управляющие-структуры)\n    - [If](#if)\n    - [Переопределение и переприсваивание](#переопределение-и-переприсваивание)\n    - [Оператор For](#оператор-for)\n    - [Switch(переключатель)](#switchпереключатель)\n    - [Переключатель типов (Типизированный переключатель, Type switch)](#переключатель-типов-типизированный-переключатель-type-switch)\n  - [Функции и методы(Functions, методы)](#функции-и-методыfunctions-методы)\n    - [Множественное возвращение результатов](#множественное-возвращение-результатов)\n    - [Именование параметров результата](#именование-параметров-результата)\n    - [Отсроченный вызов (Defer)](#отсроченный-вызов-defer)\n  - [Данные](#данные)\n    - [Созданные с помощью `new`](#созданные-с-помощью-new)\n    - [Конструкторы и составные литералы](#конструкторы-и-составные-литералы)\n    - [Создание с помощью `make`](#создание-с-помощью-make)\n    - [Массивы](#массивы)\n    - [Срезы(Slices, слайсы)](#срезыslices-слайсы)\n    - [Двухмерные срезы](#двухмерные-срезы)\n    - [Карты(Maps)](#картыmaps)\n    - [Печать(Printing)](#печатьprinting)\n    - [Присоединение(Append)](#присоединениеappend)\n  - [Инициализация(Initialization)](#инициализацияinitialization)\n    - [Константы(Constants)](#константыconstants)\n    - [Переменные(Variables)](#переменныеvariables)\n    - [Функция init](#функция-init)\n  - [Методы(Methods)](#методыmethods)\n    - [Указатели или Значения](#указатели-или-значения)\n  - [Интерфейсы и другие типы](#интерфейсы-и-другие-типы)\n    - [Интерфейсы](#интерфейсы)\n    - [Преобразование (Conversions)](#преобразование-conversions)\n    - [Конвертация интерфейсов и привязка типов](#конвертация-интерфейсов-и-привязка-типов)\n    - [Общее(Generality)](#общееgenerality)\n    - [Интерфейсы и методы(функции)](#интерфейсы-и-методыфункции)\n  - [Пустой идентификатор (The blank identifier **\\_**)](#пустой-идентификатор-the-blank-identifier-_)\n    - [Пустой идентификатор в множественном присваивании (**\\_**)](#пустой-идентификатор-в-множественном-присваивании-_)\n    - [Неиспользуемое импортирование и значения](#неиспользуемое-импортирование-и-значения)\n    - [Импортирование для побочного эффекта (Import for side effect)](#импортирование-для-побочного-эффекта-import-for-side-effect)\n    - [Проверка интерфейса (Interface checks)](#проверка-интерфейса-interface-checks)\n  - [Вложение (Embedding)](#вложение-embedding)\n  - [Согласованность, параллельная обработка, параллельное выполнение  (Concurrency)](#согласованность-параллельная-обработка-параллельное-выполнение--concurrency)\n    - [Распределение памяти по сообщениям (Share by communicating)](#распределение-памяти-по-сообщениям-share-by-communicating)\n    - [Го-рутины (Goroutines)](#го-рутины-goroutines)\n    - [Каналы (Channels)](#каналы-channels)\n    - [Канал каналов (Channels of channels)](#канал-каналов-channels-of-channels)\n    - [Параллелизм (Parallelization)](#параллелизм-parallelization)\n    - [Текущий буфер (A leaky buffer)](#текущий-буфер-a-leaky-buffer)\n  - [Ошибки (Errors)](#ошибки-errors)\n    - [Паника (Panic)](#паника-panic)\n    - [Восстановление (Recover)](#восстановление-recover)\n  - [Веб-сервер](#веб-сервер)\n\n\n--------------\n\n## Введение\n\n[^](#Оглавление)\n\nGo - это новый язык программирования. Хотя, он заимствует идеи из существующих языков, он обладает необычными свойствами, которые позволяют создавать эффективные программы, язык Go отличается по своему характеру от программ, написанных на родственных языках. Прямолинейный перевод C++ или Java программ в Go вряд ли даст удовлетворительный результат, т.к. Java программы написаны на Java, не на Go. С другой стороны, думая о проблеме с точки зрения Go можно добиться успеха, но это уже другая программа. Другими словами, для хорошего написания кода на языке Go, важно понимать его особенности и идиомы.\nТакже важно знать установленные соглашения для программирования на Go, такие как именование, форматирование, разработка программ и так далее, так чтобы программы написанные Вами были простыми для понимания другими программистами Go.\n\n\n\nЭтот документ даёт примеры для написания чистого, идеоматичного кода на Go.\nОн дополняет [спецификацию языка](https://golang.org/ref/spec), [Тур по Go](https://tour.golang.org/),\nи [Как писать на Go](https://golang.org/doc/code.html), каждую из которых необходимо прочитать в первую очередь.\n\n[^](#Оглавление)\n\n### Примеры\n\n[^](#Оглавление)\n\n[Go пакеты исходных кодов](https://golang.org/src/) предназначены не только в качестве основных библиотек, но и в качестве примеров использования языка.\nКроме того, многие пакеты имеют работающие, автономные исполняемые примеры и Вы можете запустить напрямую с помощью страницы [golang.org](https://golang.org), такие как [этот](https://golang.org/pkg/strings/#example_Map) (если  необходимо, нажмите на слово \"Примеры\" чтобы открыть их).\nЕсли у Вас есть вопрос о том как решить какую-либо проблему или как что-то реализовать, то документация, исходные коды и примеры в библиотеке могут дать ответ, идею или объяснение.\n\n[^](#Оглавление)\n\n## Форматирование\n\n[^](#Оглавление)\n\nФорматирование является наиболее спорным, но не сильно важным вопросом.\nЛюди могут привыкнуть к различным стилям форматирования, но было бы лучше, если бы этого не приходилось делать и меньше времени придавалось этой теме, если бы все использовали одинаковый стиль.\nПроблема данной утопии в том, как это сделать без длинного руководства по стилю.\n\nВ Go мы используем нетипичный подход и передаем машине заботу о форматировании. \nПрограмма `gofmt` (также доступна, как `go fmt`, которая производит действия на уровне пакета, а не на уровне файлов) читает код на Go и выпускает исходный код со стандартным стилем отступов и вертикальным выравниванием, сохраняет, и при необходимости, переформатирует комментарии.\nЕсли Вы хотите знать, как по-новому структурировать код, запустите `gofmt`; если структура неверна, `gofmt` поправит Вашу программу (или файл сообщит об ошибке `gofmt`), не работайте в обход форматирования программой `gofmt`.\n\nК примеру, нет необходимости тратить время на выравнивание комментариев для полей структур, т.к. `gofmt` сделает это за Вас. \nДля данного фрагмента кода\n\n```golang\ntype T struct {\n    name string // name of the object\n    value int // its value\n}\n```\n\n`gofmt` произведет выравнивание по колонкам:\n\n```golang\ntype T struct {\n    name    string // name of the object\n    value   int    // its value\n}\n```\n\nВсе стандартные пакеты Go отформатированы с помощью `gofmt`.\n\nОчень коротко о некоторых деталях форматирования:\n\n#### Абзац\nМы используем табуляцию для абзацев и `gofmt` делает это по умолчанию. Используйте пробелы только при острой необходимости.\n#### Длина строки\nGo не имеет предела длины строки. Не беспокойтесь о длинных строках. Если строка кажется слишком длинной, прервите ее и добавьте дополнительный отступ (символ табуляции) на новой строке.\n#### Круглые скобки\nGo нуждается в меньшем количестве круглых скобок, чем C и Java: структуры ветвления, цикла ( `if` , `for` , `switch`) не имеют круглых скобок в своём синтаксисе. Также, иерархия операторов стала проще и короче. К примеру, выражение\n\n```\nx\u003c\u003c8 + y\u003c\u003c16\n```\nне нуждается в добавлении пробелов, в отличии от других языков.\n\n[^](#Оглавление)\n\n\n## Комментарии\n\n[^](#Оглавление)\n\nGo использует C-стиль `/* */` для блока комментариев и C++-стиль `//` для однострочных комментариев.\nКак правило, используются однострочные комментарии. Блок комментариев, в основном, используется при комментировании пакетов, \nно также для выразительности или отключения большого участка кода.\n\nПрограмма и веб-сервер - `godoc` обрабатывает Go исходники пакета для формирования документации.\nКомментарии, расположенные сразу над объявлением (без дополнительных пустых строк), извлекаются вместе с объявлением для пояснения данного элемента.\nХарактер и стиль комментариев напрямую влияет на качество документации производимой `godoc`.\n\nКаждый пакет должен иметь *комментарий пакета* - это блок комментариев предшествующий объявлению пакета.\nДля пакетов состоящих из нескольких файлов, комментарий пакета может быть расположен в любом из файлов, но только в одном из них.\nКомментарий пакета должен представлять информацию о пакете в целом. \nОн будет отображен вначале страницы `godoc` и должен представлять из себя детальную информацию, которой можно пользоваться.\n\n```golang\n/*\nPackage regexp implements a simple library for regular expressions.\n\nThe syntax of the regular expressions accepted is:\n\n    regexp:\n        concatenation { '|' concatenation }\n    concatenation:\n        { closure }\n    closure:\n        term [ '*' | '+' | '?' ]\n    term:\n        '^'\n        '$'\n        '.'\n        character\n        '[' [ '^' ] character-ranges ']'\n        '(' regexp ')'\n*/\npackage regexp\n```\n\nЕсли пакет простой, то комментарий может быть кратким.\n\n```golang\n// Package path implements utility routines for\n// manipulating slash-separated filename paths.\n```\n\nДополнительное форматирование, к примеру баннер из * (звездочек), не требуется.\nШрифт для сформированного результата не обязательно будет моноширинный, поэтому не полагайтесь на пробелы при выравнивании, `godoc`, также как `gofmt`, позаботятся об этом.\nКомментарии интерпретируются как простой текст, поэтому HTML и другие аннотации такие как `_эта_` воспроизводятся *дословно* и поэтому не должны использоваться. Единственное исключение,\nкоторое делает `godoc`, это выделение моноширинным шрифтом участков кода с отступами.\nХорошим примером такого исключения является комментарий к пакету [`fmt`](https://golang.org/pkg/fmt/).\n\nВ зависимости от контекста, `godoc` не может переформатировать комментарии, поэтому убедитесь, что они выглядят хорошо: используйте правильное правописание, знаки препинания, структуру предложения и т.д.\n\nЛюбые комментарии внутри пакета, предшествующие объявлению, используются как описание этого объявления.\nКаждый экспортируемый объект, название которого начинается с большой буквы, должен иметь комментарий.\n\nЛучше всего использовать комментарии в виде полных предложений. Это позволяет производить их автоматическую обработку.\nПервое предложение должно быть ключевым и начинаться с имени объявления.\n\n```golang\n// Compile parses a regular expression and returns, if successful,\n// a Regexp that can be used to match against text.\nfunc Compile(str string) (*Regexp, error) {\n```\n\nЕсли комментарий начинается с имени, то `godoc` может с использоваться совместно с `grep`.\nПредставьте, что Вы не можете вспомнить имя \"Compile\", но Вы ищите *the parsing function* для регулярных выражений и тогда Вы можете выполнить команду:\n\n```command\n$ godoc regexp | grep -i parse\n```\n\nЕсли все комментарии в пакете начинаются с \"This function...\", `grep` не сможет помочь с поиском имени.\nЕсли же комментарии  начинаются с имени, Вы можете увидеть что-то вроде следующего результата, который напомнит Вам о том, что Вы искали.\n\n```command\n$ godoc regexp | grep parse\n    Compile parses a regular expression and returns, if successful, a Regexp\n    parsed. It simplifies safe initialization of global variables holding\n    cannot be parsed. It simplifies safe initialization of global variables\n$\n```\n\nСинтаксис Go допускает групповое объявление. Для каждой группы констант или переменных может быть представлен один общий комментарий. Однако такое объявление выглядит небрежно.\n\n```golang\n// Error codes returned by failures to parse an expression.\nvar (\n    ErrInternal      = errors.New(\"regexp: internal error\")\n    ErrUnmatchedLpar = errors.New(\"regexp: unmatched '('\")\n    ErrUnmatchedRpar = errors.New(\"regexp: unmatched ')'\")\n    ...\n)\n```\n\nГруппировка также может показать взаимосвязи между элементами, к примеру, группа переменных защищенных mutex:\n\n```golang\nvar (\n    countLock   sync.Mutex\n    inputCount  uint32\n    outputCount uint32\n    errorCount  uint32\n)\n```\n\n[^](#Оглавление)\n\n## Именование\n\n[^](#Оглавление)\n\nИменование очень важно в Go, как и в других языках.\nОни имеют семантический эффект: **Видимость имени за пределами пакета, определяется по первой букве имени, которая, если является заглавной, то имя будет видно вне это пакета**.\nИменно поэтому стоит уделить время обсуждению соглашения об именовании в программах Go.\n\n### Именование пакетов\n\n[^](#Оглавление)\n\nКогда пакет импортируется, имя пакета используется для доступа к его содержимому.\nПосле того, как пакет импортирован, \n\n```golang\nimport \"bytes\"\n```\n\nможно использовать `bytes.Buffer`. Это полезно, если все, кто использует пакет, могут использовать одно и то же имя, для обращения к его содержимому, подразумевается, что имя пакета должно быть коротким, четким и запоминающимся. В соответствии с соглашением,имена пакетов состоят из одного слова в нижнем регистре; нет необходимости в использовании подчеркиваний или СмешанногоРегистра. При выборе длинного имени пакета, всем, кто будет его использовать, придётся писать это имя. Но не беспокойтесь об уникальности имени. \nИмя пакета только по умолчанию используется при импорте; оно не должно быть глобально уникальным, и в редких случаях, при импорте может быть указано другое имя. В любом случае,\nпутаница встречается редко, так как имя файла в импорте определяет, какой именно пакет используется.\n\nСогласно другому соглашению, имя пакета является базовым именем его исходного каталога; пакет `src/encoding/base64` импортируется как `\"encoding/base64\"` и имеет название `base64`, а не `encoding_base64` и не `encodingBase64`.\n\nИмпортирующий пакет будет использовать имя пакета для обозначения его содержимого, поэтому при экспорте может учитываться этот факт, чтобы избежать повторения. \n(Не используйте `import .`, это, конечно, может упростить запуск тестов вне пакета, но в других случаях использоваться не должно). Например, тип *reader* для буферного чтения описанный в пакете `bufio` называется `Reader`, а не `BufReader`, т.к пользователи его видят как `bufio.Reader`, имя которого кратко и понятно.\n\n\n\nБолее того, т.к. импортируемые объекты адресуются по имени пакета, следовательно `bufio.Reader` не будет конфликтовать с `io.Reader`. \nАналогично, функция для создания нового экземпляра объекта `ring.Ring`, которая объявлена как *конструктор* в Go, может называться `NewRing`, но т.к. `Ring` - это экспортируемый тип из пакета `ring`, функция-конструктор может называться просто `New`, которую, можно будет вызвать как `ring.New`. Используйте структуру пакетов при \nвыборе имен.\n\nДругой короткий пример функция `once.Do`; `once.Do(setup)` читается хорошо, и при этом\nлучше не станет, если ее переименовать в `once.DoOrWaitUntilDone(setup)`.\nДлинные имена не делают названия более читабельными. В то время как комментарии\nмогут быть более ценным, чем длинные имена.\n\n## Геттеры\n\n[^](#Оглавление)\n\nGo не предоставляет автоматическую поддержку геттеров и сеттеров.\nНо не будет ошибкой создание геттеров и сеттеров самостоятельно, и если это необходимо, то делайте так, но идиоматически нет необходимости добавлять `Get` в имя геттера. \nЕсли у Вас есть поле с именем `owner` (с маленькой буквы, неэкспортируемое), то геттер может называться `Owner` (с большой буквы, экспортируемый), а не `GetOwner`.\nИспользование имен, начинающихся с заглавной буквы, позволяет отделить экспортируемые методы от неэкспортируемых полей. Cеттер, при необходимости, может быть назван `SetOwner`.\nОба примера в следующем коде:\n\n```golang\nowner := obj.Owner()\nif owner != user {\n    obj.SetOwner(user)\n}\n```\n\n### Имена интерфейсов\n\n[^](#Оглавление)\n\nПо соглашению, интерфейсы с одним методом должны называться как метод с суффиксом `-er` или подобно этому,  для образования существительного: `Reader`, `Writer`, `Formatter`, `CloseNotifier` и т.д.\n\nСуществует целый ряд имен, которыe соблюдают это соглашение и содержат подобные методы. `Read` , `Write` , `Close`, `Flush`, `String` и т.д., имеют канонические подписи и значения. Чтобы избежать путаницы, не давайте методу ни одного из этих имен, если оно не имеет ту же сигнатуру и значение. С другой стороны, если ваш тип реализует метод с тем же значением, как и метод хорошо известного типа, то дайте ему то же имя и значение; назовите Ваш метод конвертации в строку `String` , а не `ToString`.\n\n### MixedCaps\n\n[^](#Оглавление)\n\nВ заключении, Go соглашение использует `MixedCaps` или `mixedCaps` , а не подчеркивание для имен из нескольких слов.\n\n\n### Точка с запятой\n\n[^](#Оглавление)\n\nКак и в С, грамматика Go формально использует точку с запятой для разделения \nопераций-выражений (инструкций), но в отличии от C, точка с запятой не представлена \nв исходном коде. Вместо этого, лексер использует простое правило добавления \nточки с запятой автоматически, при сканировани. Таким образом текст на входе \nпо большей части освобожден от них.\n\nПравило такое. Если последний токен(лексема) перед символом новой строки - идентификатор (который включает такие слова, как `int` и `float64`), базовый литерал, такой как число или строковая константа, или один из нижеперечисленных токенов\n\n```golang\nbreak continue fallthrough return ++ -- ) }\n```\n\nто, лексер всегда добавляет точку с запятой после него. Вкратце, это может звучать так: \"Если новая строка начинается после токена, который может закрывать операцию-выражение,  то добавить точку с запятой\".\n\nТочка с запятой также может быть опущена сразу перед закрывающей скобкой, таким\nобразом для операции-выражения такой как:\n\n```golang\n     go func() { for { dst \u003c- \u003c-src } }()\n```\n\nточка с запятой не требуется.\n\nКак следствие из правила, вы не можете перенести открывающую скобку управляющих\nструктур (`if`, `for`, `switch` или `select`) на новую строку. Если перенесете,\nточка с запятой будет вставлена перед скобкой, которая может стать причиной \nнежелательных эффектов. Пишите так,\n\n```golang\nif i \u003c f() {\n    g()\n}\n```\n\nно не так\n\n```golang\nif i \u003c f()  // ошибка!\n{           // ошибка!\n    g()\n}\n```\n\n[^](#Оглавление)\n\n\n## Управляющие структуры\n\n[^](#Оглавление)\n\nУправляющие структуры в Go аналогичны тем же структурам в C, но имеют ряд важных отличий. Во-первых нет циклов `do` и `while`, есть лишь обобщенный `for`. Во-вторых, `switch` более гибкий. В-третьих `if` и `switch` имеют опциональную инициализацию переменных, как и в `for`. В-четвертых, `break` и `continue` опционально принимают метку, к которой необходимо перейти. В-пятых, есть новые операторы, такие как типизированный `switch` и многоканальный `select`. Синтаксис также немного отличается: отсутствуют круглые скобки в условии, и тело структуры всегда должно быть ограничено фигурными скобками.\n\n[^](#Оглавление)\n\n### If\n\n[^](#Оглавление)\n\nВ Go простой `if` выглядит так:\n\n```golang\nif x \u003e 0 {\n    return y\n}\n```\n\nОбязательные фигурные скобки упрощают написание простых условий `if` на \nнесколько строк. Это хороший стиль в любом случае, особенно когда тело содержит управляющие операторы, такие как `return` или `break`.\n\nПоскольку `if` и `switch` допускают инициализацию переменных, то часто можно \nвидеть подобную запись:\n\n```golang\nif err := file.Chmod(0664); err != nil {\n    log.Print(err)\n    return err\n}\n```\n\nВ библиотеках Go, вы найдёте подобную запись, если `if` не переходит в следующий блок, т.е. в теле используется `break`, `continue`, `goto` или `return`, а необязательный `else` опускается.\n\n```golang\nf, err := os.Open(name)\nif err != nil {\n    return err\n}\ncodeUsing(f)\n```\n\nВ данном примере представлена общая схема, где код защищен от серии ошибок. Код читается хорошо, если выполняется без ошибок, обходя случаи их возникновения. Так как ошибки приводят к завершению выполнения блока с помощью `return`, то блок `else` не требуется.\n\n```golang\nf, err := os.Open(name)\nif err != nil {\n    return err\n}\nd, err := f.Stat()\nif err != nil {\n    f.Close()\n    return err\n}\ncodeUsing(f, d)\n```\n\n[^](#Оглавление)\n\n### Переопределение и переприсваивание\n\n[^](#Оглавление)\n\nПоследний пример предыдущего раздела демонстрирует использование краткой формы объявления переменных `:=`. Вызов `os.Open` объявляет сразу две переменных `f` и `err`\n\n```golang\nf, err := os.Open(name)\n```\n\nНесколькими строками ниже вызывается `f.Stat`, \n\n```golang\nd, err := f.Stat()\n```\n\nкоторый выглядит как объявления двух переменных `d` и `err`. Хотя `err` присутствует в обоих объявлениях. Это дублирование вполне законно: `err` объявляется в первом случае, и лишь переприсваивается во втором. Это означает, что `f.Stat` использует уже существующую переменную `err`, определенную выше, и просто присваивает ей новое значение.\n\nВ объявлении `:=` переменная `v` может присутствовать, даже если она уже объявлена, при условии:\n\n  * если объявление происходит в той же самой области видимости, что и существующая переменная `v` (если `v` уже объявлена за пределами видимости, то объявление создаст новую переменную §)\n  * соответствующее значение, при инициализации, может быть присвоено `v`\n  * существует хотя бы одна новая переменная в объявлении, которая будет создана заново\n\nЭто необычное свойство - чистая практичность, которая служит для упрощения \nиспользования одной переменной `err`, к примеру, в длинных цепочках `if-else`.\nВы увидите, это используется часто.\n\n§ Нет ничего плохого в том, что в Go область видимости параметров и возвращаемых значений функции - есть само тело функции, хотя они лексически находятся за скобками, ограничивающими тело функции.\n\n[^](#Оглавление)\n\n### Оператор For\n\n[^](#Оглавление)\n\nВ Go цикл `for` очень похож, но не такой же как в C. Он унифицирует `for` и `while`, при этом отсутствует `do-while` цикл. Существует 3 различных формы, и только в одной из них используется точка с запятой.\n\n```golang\n// C-подобный for\nfor init; condition; post { }\n\n// C-подобный while\nfor condition { }\n\n// C-подобный for(;;)\nfor { }\n```\n\nКраткая запись позволяет легко объявить начальные условия прямо в цикле:\n\n```golang\nsum := 0\nfor i := 0; i \u003c 10; i++ {\n    sum += i\n}\n```\n\nЕсли Вы итерируетесь по массиву, срезу, строке или map'у, или читаете из канала, то для управления можно использовать `range`.\n\n```golang\nfor key, value := range oldMap {\n    newMap[key] = value\n}\n```\n\n\nЕсли необходимо использовать только первый элемент *диапазона* (ключ или индекс), отбросьте второй:\n\n```golang\nfor key := range m {\n    if key.expired() {\n        delete(m, key)\n    }\n}\n```\n\nЕсли вам необходим только второй элемент (значение), то используйте *пустой идентификатор* (**_**) в качестве первого элемента:\n\n```golang\nsum := 0\nfor _ , value := range array {\n    sum += value\n}\n```\n\nПустой идентификатор используется в разных случаях и будет описан позже.\n\nДля строк, оператор `range` выполняет ещё больше работы, к примеру разделяет строку по символам Unicode в соответствии с UTF-8. При ошибочном использование кодировки, побайтово заменяет рунами(*rune*) U+FFFD. (`rune` (и одноименный встроенный тип) в терминологии Go используется для работы с символами Unicode. Смотрите детальную информацию в [Спецификации языка](https://golang.org/ref/spec#Rune_literals)).\n\nДанный цикл:\n\n```golang\nfor pos, char := range \"日本\\x80語\" { // \\x80 is an illegal UTF-8 encoding\n    fmt.Printf(\"character %#U starts at byte position %d\\n\", char, pos)\n}\n```\n\nВыводит:\n\n```command\ncharacter U+65E5 '日' starts at byte position 0\ncharacter U+672C '本' starts at byte position 3\ncharacter U+FFFD '�' starts at byte position 6\ncharacter U+8A9E '語' starts at byte position 7\n```\n\nИ в заключении, в языке Go нет оператора `запятая`, а `++` и `--` являются инструкциями, но не выражениями. Таким образом, если Вам необходимо использовать несколько переменных в цикле `for`, то Вы можете использовать параллельное определение переменных (без использования `++` и `--`).\n\n```golang\n// Reverse a\nfor i, j := 0, len(a)-1; i \u003c j; i, j = i+1, j-1 {\n    a[i], a[j] = a[j], a[i]\n}\n```\n\n[^](#Оглавление)\n\n### Switch(переключатель)\n\n[^](#Оглавление)\n\nВ языке Go `switch` более обобщён, нежели в C. Выражения не обязательно должны\nбыть константами или даже целыми числами, условия проверяются сверху-вниз до нахождения соответствия, и если `switch` не имеет выражений, то переходит в `true`. Следовательно, идиоматически возможно записывать `if-else-if-else` цепочку как `switch`.\n\n```golang\nfunc unhex(c byte) byte {\n    switch {\n    case '0' \u003c= c \u0026\u0026 c \u003c= '9':\n        return c - '0'\n    case 'a' \u003c= c \u0026\u0026 c \u003c= 'f':\n        return c - 'a' + 10\n    case 'A' \u003c= c \u0026\u0026 c \u003c= 'F':\n        return c - 'A' + 10\n    }\n    return 0\n}\n```\n\nАвтоматический пропуск условий отсутствует, но, при этом, условия могут быть записаны через запятую:\n\n```golang\nfunc shouldEscape(c byte) bool {\n    switch c {\n    case ' ', '?', '\u0026', '=', '#', '+', '%':\n        return true\n    }\n    return false\n}\n```\nНесмотря на то, что они не столь распространены в Go, как в некоторых других C-подобных языках, `break` может быть использован для досрочного прерывания `switch`.\nХотя, иногда, надо прервать внешний (по отношению к `switch`) цикл, а не сам `switch`, и в Go это может быть достигнуто путём добавления метки перед циклом, и переходом к этой метке в случае вызова `break`. В следующем примере представлены оба случая:\n\n```golang\nLoop:\n\tfor n := 0; n \u003c len(src); n += size {\n\t\tswitch {\n\t\tcase src[n] \u003c sizeOne:\n\t\t\tif validateOnly {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tsize = 1\n\t\t\tupdate(src[n])\n\n\t\tcase src[n] \u003c sizeTwo:\n\t\t\tif n+1 \u003e= len(src) {\n\t\t\t\terr = errShortInput\n\t\t\t\tbreak Loop\n\t\t\t}\n\t\t\tif validateOnly {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tsize = 2\n\t\t\tupdate(src[n] + src[n+1]\u003c\u003cshift)\n\t\t}\n\t}\n```\n\nКонечно, `continue` также допускает использование меток, но только в циклах.\n\nВ заключении, метод сравнения байтовых срезов использующий два оператора `switch`:\n\n```golang\n// Compare returns an integer comparing the two byte slices,\n// lexicographically.\n// The result will be 0 if a == b, -1 if a \u003c b, and +1 if a \u003e b\nfunc Compare(a, b []byte) int {\n    for i := 0; i \u003c len(a) \u0026\u0026 i \u003c len(b); i++ {\n        switch {\n        case a[i] \u003e b[i]:\n            return 1\n        case a[i] \u003c b[i]:\n            return -1\n        }\n    }\n    switch {\n    case len(a) \u003e len(b):\n        return 1\n    case len(a) \u003c len(b):\n        return -1\n    }\n    return 0\n}\n```\n\n[^](#Оглавление)\n\n### Переключатель типов (Типизированный переключатель, Type switch)\n\n[^](#Оглавление)\n\n`switch` может быть использован для определения динамических типов интерфейсных переменных. Так, типизированный `switch` использует синтаксис приведения типов,\nс ключевым словом `type` внутри скобок. Если `switch` объявляет переменную в \nвыражении, то переменная будет иметь соответствующий тип в каждом пункте. Также, идиоматически верно переиспользовать имена переменных для объявления новых переменных\nс тем же именем, но другим типом в каждом случае:\n\n```golang\nvar t interface{}\nt = functionOfSomeType()\nswitch t := t.(type) {\ndefault:\n    fmt.Printf(\"unexpected type %T\\n\", t)     // %T prints whatever type t has\ncase bool:\n    fmt.Printf(\"boolean %t\\n\", t)             // t has type bool\ncase int:\n    fmt.Printf(\"integer %d\\n\", t)             // t has type int\ncase *bool:\n    fmt.Printf(\"pointer to boolean %t\\n\", *t) // t has type *bool\ncase *int:\n    fmt.Printf(\"pointer to integer %d\\n\", *t) // t has type *int\n}\n```\n\n## Функции и методы(Functions, методы)\n\n[^](#Оглавление)\n\n### Множественное возвращение результатов\n\n[^](#Оглавление)\n\nОдно из особенностей языка Go - это то, что функции и методы могут возвращать множество значений.\n\nПри использовании языка С, передача ошибки производится через отрицательное значение с описанием причины ошибки в \"другом\" месте.\n\nПри использовании языка Go, функция `Write` может вернуть одновременно **и** возвращаемое значение **и** ошибку.\nСигнатура метода `Write` в файлах пакета `os`:\n\n```golang\nfunc (file *File) Write(b []byte) (n int, err error)\n```\n\nи как предусмотрено документацией, он возвращает число записанных байт и ненулевое значение ошибки `error`, когда `n` `!=` `len(b)`.\nЭто общий стиль, смотрите также раздел посвящённый ошибкам в качестве примера.\n\nДанный подход исключает необходимость в возращении значимого параметра.\nЭто очень простой способ возвращения из функции количества байт среза, возвращая число и следующий параметр.\n\n```golang\nfunc nextInt(b []byte, i int) (int, int) {\n    for ; i \u003c len(b) \u0026\u0026 !isDigit(b[i]); i++ {\n    }\n    x := 0\n    for ; i \u003c len(b) \u0026\u0026 isDigit(b[i]); i++ {\n        x = x*10 + int(b[i]) - '0'\n    }\n    return x, i\n}\n```\n\nВы можете сканировать число чисел во входном срезе `b` следующим образом:\n\n\n```golang\n    for i := 0; i \u003c len(b); {\n        x, i = nextInt(b, i)\n        fmt.Println(x)\n    }\n```\n\n### Именование параметров результата\n\n[^](#Оглавление)\n\nВозвращаемым \"параметрам\" в языке Go можно давать имена и это часто используется как входные параметры.\nКогда они именованы, то они инициализируются нулевым значением необходимого типа в самом начале функции.\nЕсли функция, в которой определены именованные параметры, вызывает конструкцию возврата без аргументов, то значения именованных параметров будут использованы ей как возвращаемые значения.\nИменование не обязательное, но оно может сделать код короче и чище - самодокументированным.\nЕсли имя результата будет `nextInt`, то очевидно что тип результата `int`.\n\n\n```golang\nfunc nextInt(b []byte, pos int) (value, nextPos int) {\n```\n\nНа примере `io.ReadFull`:\n\n\n```golang\nfunc ReadFull(r Reader, buf []byte) (n int, err error) {\n    for len(buf) \u003e 0 \u0026\u0026 err == nil {\n        var nr int\n        nr, err = r.Read(buf)\n        n += nr\n        buf = buf[nr:]\n    }\n    return\n}\n```\n\n[^](#Оглавление)\n\n### Отсроченный вызов (Defer)\n\n[^](#Оглавление)\n\nВ языке Go есть оператор `defer` для управления отложенного вызова функции, который будет вызван, как только функция имеющая `defer` оканчивается.\nЭто не типичный но эффективный способ, когда необходимо закрыть ресурс после окончания функции.\nКанонические примеры - работа с mutex или закрытие файла.\n\n```golang\n// Contents returns the file's contents as a string.\nfunc Contents(filename string) (string, error) {\n    f, err := os.Open(filename)\n    if err != nil {\n        return \"\", err\n    }\n    defer f.Close()  // f.Close will run when we're finished.\n\n    var result []byte\n    buf := make([]byte, 100)\n    for {\n        n, err := f.Read(buf[0:])\n        result = append(result, buf[0:n]...) // append is discussed later.\n        if err != nil {\n            if err == io.EOF {\n                break\n            }\n            return \"\", err  // f will be closed if we return here.\n        }\n    }\n    return string(result), nil // f will be closed if we return here.\n}\n```\n\nОтложенный вызов функции `Close` имеет 2 преимущества. Во-первых, гарантирует что не будет забыто закрытие файла - ошибка,  которую легко сделать, если в последствии в функции будет изменен параметр на другую папку. Во-вторых, закрытие близко расположено к открытию, что более ясно, чем располагать его в конце функции.\n\nАргументы отложенной функции выполняются когда выполняется `defer`, а не когда функция вызвана.\nКроме того , во избежания беспокойства по поводу изменяющихся переменных в функции, одна отложенная функция может отложить вызов множества функций.\n\nВот простой пример:\n\n```golang\nfor i := 0; i \u003c 5; i++ {\n    defer fmt.Printf(\"%d \", i)\n}\n```\n\nОткладывание функции в LIFO очередь, приведет к следующей работе функции при печати на экран `4 3 2 1 0` . Более интересный пример - простое отслеживание функции в программе. Мы могли бы написать простое отслеживание, как это:\n\n\n```golang\nfunc trace(s string)   { fmt.Println(\"entering:\", s) }\nfunc untrace(s string) { fmt.Println(\"leaving:\", s) }\n\n// Use them like this:\nfunc a() {\n    trace(\"a\")\n    defer untrace(\"a\")\n    // do something....\n}\n```\n\nМы могли бы сделать лучше - используя факт отложенных функций для оценки когда будет запущен `defer`. Отслеживаемая функция может настроить аргументы неотслеживаемой функции.\nК примеру:\n\n```golang\nfunc trace(s string) string {\n    fmt.Println(\"entering:\", s)\n    return s\n}\n\nfunc un(s string) {\n    fmt.Println(\"leaving:\", s)\n}\n\nfunc a() {\n    defer un(trace(\"a\"))\n    fmt.Println(\"in a\")\n}\n\nfunc b() {\n    defer un(trace(\"b\"))\n    fmt.Println(\"in b\")\n    a()\n}\n\nfunc main() {\n    b()\n}\n```\n\n\nвыводит:\n\n\n```\nentering: b\nin b\nentering: a\nin a\nleaving: a\nleaving: b\n```\n\nДля программистов привыкших к блочному управлению ресурсами в других языках, функция `defer` может показаться странной, но интересной и мощной, так как позволяет уйти от блочного управления к управлению в функции. В разделах `panic` и `recover` будут также рассматриваться несколько примеров.\n\n[^](#Оглавление)\n\n\n## Данные\n\n[^](#Оглавление)\n\n### Созданные с помощью `new`\n\n[^](#Оглавление)\n\nДля создания примитивов в языке Go используются функции `new` и `make`.\nОни разные и применяются для разных типов, это может сбить с толку, но правило очень просто.\nДля начала обсудим функцию `new`.\nДанная функция резервирует память, но не также как в других языках программирования, она не просто *инициализирует* память, а вместо этого заполняет *нулями*.\n\nК примеру `new(T)` резервирует память нулями для нового элемента типа `T` и возвращает его указатель на значение типа `*T`. В терминологии Go, он возвращает указатель на новую зарезервированную память заполненная нулями с типом `T`.\n\n\n**TODO**\nSince the memory returned by `new` is zeroed, it's helpful to arrange when designing your data structures that the zero value of each type can be used without further initialization.  This means a user of the data structure can create one with `new` and get right to work.\nFor example, the documentation for `bytes.Buffer` states that \"the zero value for `Buffer` is an empty buffer ready to use.\"\nSimilarly, `sync.Mutex` does not have an explicit constructor or `Init` method.\nInstead, the zero value for a `sync.Mutex` is defined to be an unlocked mutex.\nThe zero-value-is-useful property works transitively. Consider this type declaration.\n**-**\n\n```golang\ntype SyncedBuffer struct {\n    lock    sync.Mutex\n    buffer  bytes.Buffer\n}\n```\n\n**TODO**\nValues of type `SyncedBuffer` are also ready to use immediately upon allocation or just declaration.  In the next snippet, both `p` and `v` will work correctly without further arrangement.\n**-**\n\n```golang\np := new(SyncedBuffer)  // type *SyncedBuffer\nvar v SyncedBuffer      // type  SyncedBuffer\n```\n\n### Конструкторы и составные литералы\n\n[^](#Оглавление)\n\nИногда нулевых значений не достаточно и необходимо иметь конструктор, следующий пример взят из пакета `os`.\n\n```golang\nfunc NewFile(fd int, name string) *File {\n    if fd \u003c 0 {\n        return nil\n    }\n    f := new(File)\n    f.fd = fd\n    f.name = name\n    f.dirinfo = nil\n    f.nepipe = 0\n    return f\n}\n```\n\nСуществует много шаблонов. Мы просто можем использовать *составные литералы*, которые будут создавать новые сущности каждый раз.\n\n```golang\nfunc NewFile(fd int, name string) *File {\n    if fd \u003c 0 {\n        return nil\n    }\n    f := File{fd, name, nil, 0}\n    return \u0026f\n}\n```\n\nОбратите внимание на то, что в отличии от языка С, это нормально, возвращать адрес локальных переменных, так как переменная уже существует после возвращения из функции.\nНа самом деле, возвращение адресов составных литералов создает новую сущность каждый раз, как он вычисляется.\nИтак мы можем объединить последние две строки:\n\n```golang\n    return \u0026File{fd, name, nil, 0}\n```\n\nПоля составных литералов должны быть в порядке объявления и все должны присутствовать.\nОднако, используя маркировку как пара *поле*`:`*значение*, могут инициализироваться в любом порядке, с пропущенными полями заполняемые нулями.\nТаким образом, можно объявить:\n\n```golang\n    return \u0026File{fd: fd, name: name}\n```\n\nВ предельном случае, когда составной литерал без полей вообще, то создание нулевым значением будет тип. Выражения `new(File)` и `\u0026File{}` одинаковы.\n\n\nСоставные литералы могут также создавать массивы, срезы, карты, с пометкой полей как индексов или ключами карт.\nК примеру, инициализированные значения `Enone`, `Eio`, и `Einval` разные.\n\n\n```golang\na := [...]string   {Enone: \"no error\", Eio: \"Eio\", Einval: \"invalid argument\"}\ns := []string      {Enone: \"no error\", Eio: \"Eio\", Einval: \"invalid argument\"}\nm := map[int]string{Enone: \"no error\", Eio: \"Eio\", Einval: \"invalid argument\"}\n```\n\n[^](#Оглавление)\n\n### Создание с помощью `make`\n\n[^](#Оглавление)\n\nВозвращаясь к созданию элементов.\nВстроенная функция `make(T, `*args*`)` служит для других целей нежели `new(T)`.\nОн создает только срезы, карты и каналы, и возвращают *инициализированные* (не нулевые) значение типа `T` (а не `*T`).\nПричиной различия для этих трех типов, в том что внутри они представляют из себя структуры данных, которые необходимо инициализировать перед использованием.\nК примеру, срезы - это трехэлементная структура, содержащая указатель на данные(внутри массив), длину, и емкость, причём пока все элементы не инициализированы - срез  *нулевой* `nil`.\nДля срезов, карт и каналов, встроенная команда `make` инициализирует внутреннюю структуру данных и подготавливает значения к использованию.\n\nК примеру:\n\n```golang\nmake([]int, 10, 100)\n```\n\nсоздает массив из 100 значений типа `int` и затем создает структуру среза длинной 10 и емкостью 100 со ссылкой только на первые 10 элементов.\n(Когда создается слайс, его емкость задавать не обязательно, смотрите раздел посвящённый срезам.)\nВ противоположность, `new([]int)` возвращает указатель на новый, созданный, заполненный нулями срез, это указатель на значение `nil` среза.\n\nЭти примеры показывают различие между `new` и `make`.\n\n\n```golang\nvar p *[]int = new([]int)       // allocates slice structure; *p == nil; rarely useful\nvar v  []int = make([]int, 100) // the slice v now refers to a new array of 100 ints\n\n// Unnecessarily complex:\nvar p *[]int = new([]int)\n*p = make([]int, 100, 100)\n\n// Idiomatic:\nv := make([]int, 100)\n```\n\nПомните что `make` используется только для карт, срезов и каналов и не возвращают указатель.\nДля получения указателя в явном виде используйте `new` или  возьмите указатель в явном виде.\n\n[^](#Оглавление)\n\n### Массивы\n\n[^](#Оглавление)\n\nМассивы популярны когда точно известно необходимое количество памяти, чтобы не делать излишних пересозданий, но в первую очередь они являются составной частью для срезов, о которых будет описано в следующем разделе.\n\nКакие основные отличия между обращением с массивами между языками Go и C:\n* Массивы значений. Присвоение одно массива другому копирует все элементы.\n* Если вы передаёте массив в функцию, то передаётся копия массива, а не указатель на него.\n* Размер массива является частью массива. Типы `[10]int` и `[20]int` разные.\n\nМассивы могут быть полезными, но дорогими(с точки зрения производительности) и если Вы хотите иметь гибкость и эффективность схожее с поведением в языке C-like, то необходимо использовать в функциях указатели.\n\n\n```golang\nfunc Sum(a *[3]float64) (sum float64) {\n    for _, v := range *a {\n        sum += v\n    }\n    return\n}\n\narray := [...]float64{7.0, 8.5, 9.1}\nx := Sum(\u0026array)  // Note the explicit address-of operator\n```\n\nНо данный стиль не подходит Go.\nИспользуйте срезы вместо массивов.\n\n\n[^](#Оглавление)\n\n### Срезы(Slices, слайсы)\n\n[^](#Оглавление)\n\nСрезы это обёртка для массивов и при этом более общий и мощный, и предоставляет собой более удобный интерфейс по управлению данными, в случаях, когда не известно точное количество элементов и необходимо преобразование размера массивов.\nБольшинство программ на языке Go, выполнены с использованием срезов, а не простых массивов.\n\nСрез хранит ссылку на массив и поэтому если приравнять срез к другому срезу, то будет тот же массив.\nЕсли срез является аргументом функции, то изменения элементов в срезе будут видны вызывающему данному функцию, это аналогично передаче указателя на базовый массив.\nВ функция `Read` может принимать в качестве аргумента срез, что равнозначно указателю на массив и длины массива; длина среза указывает верхний предел количество данных которые необходимо прочитать.\nВ данном случае тип `File` пакета `os` имеет следующую сигнатуру метода `Read`:\n\n```golang\nfunc (f * File) Read(buf []byte) (n int, err error)\n```\n\nМетод возвращает количество прочитанных байт или если есть, то ошибку.\nДля чтения первых 32 байт в буфере `buf`, *получить(срезать) часть* буфера.\n\n```golang\n    n, err := f.Read(buf[0:32])\n```\n\nТакой срез является эффективным. На самом деле, если оставить в стороне эффективность, то следующий пример показывает чтение первых 32 байт из буфера.\n\n```golang\n    var n int\n    var err error\n    for i := 0; i \u003c 32; i++ {\n        nbytes, e := f.Read(buf[i:i+1])  // Read one byte.\n        if nbytes == 0 || e != nil {\n            err = e\n            break\n        }\n        n += nbytes\n    }\n```\n\nДлина среза может меняться, пока не исчерпает размер внутреннего массива.\nС помощью встроенной функции `cap` можно узнать *емкость* среза, представляющий максимальную длину среза.\nВ следующем примере рассматривается функция для добавления данных в срез.\nЕсли данные превышают ёмкость среза, то срез необходимо переопределить.\nФункция `Append` возвращает результирующий срез. Функция использует тот факт что  использование `len` и `cap` допустимо, даже если у нас имеется нулевой срез `nil` - при этом возвращая 0.\n\n```golang\nfunc Append(slice, data []byte) []byte {\n    l := len(slice)\n    if l + len(data) \u003e cap(slice) {  // reallocate\n        // Allocate double what's needed, for future growth.\n        newSlice := make([]byte, (l+len(data))* 2)\n        // The copy function is predeclared and works for any slice type.\n        copy(newSlice, slice)\n        slice = newSlice\n    }\n    slice = slice[0:l+len(data)]\n    for i, c := range data {\n        slice[l+i] = c\n    }\n    return slice\n}\n```\n\n\n**TODO**\nWe must return the slice afterwards because, although `Append`\ncan modify the elements of `slice`, the slice itself (the run-time data\nstructure holding the pointer, length, and capacity) is passed by value.\n**-**\n\nДобавление элементов в срез настолько популярно, что функция `append` стала встроенной. Для того чтобы понять принцип работы данной функции нам необходимо больше информации, поэтому мы вернёмся к этому позже.\n\n[^](#Оглавление)\n\n\n### Двухмерные срезы\n\n[^](#Оглавление)\n\nМассивы и срезы в Go - одномерные.\nДля создания двухмерного массива или среза, нам необходимо определять массив-массивов или срез-срезов, как в примере:\n\n```golang\ntype Transform [3][3]float64  // A 3x3 array, really an array of arrays.\ntype LinesOfText [][]byte     // A slice of byte slices.\n```\n\nВ связи с тем, что срезы переменной длины, то допустимо иметь каждый внутренний срез разной длины.\nЭто наиболее общая ситуация, как в примере `LinesOfText`, в котором каждая строка имеет независимую длину.\n\n```golang\ntext := LinesOfText{\n\t[]byte(\"Now is the time\"),\n\t[]byte(\"for all good gophers\"),\n\t[]byte(\"to bring some fun to the party.\"),\n}\n```\n\nИногда необходимо создавать двухмерные срезы, к примеру при обработки пикселей.\nЕсть 2 способа для этого:\n  * Первый, создание каждого среза независимо\n  * Второй, создание простого массива срезов.\nНаилучший способ выбирается в зависимости от программы.\nЕсли срез можно увеличивать или уменьшать, они должны быть независимы, для того чтобы избежать перезаписи новых строк. Если не требуется изменять размер, то наиболее эффективным был бы способ с создание одним их аллоцированием(инициализацией).\nРассмотрим оба способа.\n\n```golang\n// Allocate the top-level slice.\npicture := make([][]uint8, YSize) // One row per unit of y.\n// Loop over the rows, allocating the slice for each row.\nfor i := range picture {\n\tpicture[i] = make([]uint8, XSize)\n}\n```\n\nс одним созданием:\n\n```golang\n// Allocate the top-level slice, the same as before.\npicture := make([][]uint8, YSize) // One row per unit of y.\n// Allocate one large slice to hold all the pixels.\npixels := make([]uint8, XSize*YSize) // Has type []uint8 even though picture is [][]uint8.\n// Loop over the rows, slicing each row from the front of the remaining pixels slice.\nfor i := range picture {\n\tpicture[i], pixels = pixels[:XSize], pixels[XSize:]\n}\n```\n\n[^](#Оглавление)\n\n### Карты(Maps)\n\n[^](#Оглавление)\n\nКарты - это удобная и мощная встроенная структура данных, связывающая значение одного типа(*ключ (key)*) со значением другого типа (*элемент (element)* или *значение (value)*).\nКлюч может быть любого типа, для которого определён оператор равно, как для целых чисел, чисел с плавающей точкой или комплексные числа, строки, указатели, интерфейсы (если динамические типы поддерживают равенство), структуры и массивы.\nСрезы не используются в качестве ключа для карт, так как равенство не определено для них.\nКарты, также как и срезы, имеют внутреннюю структуру данных.\nЕсли Вы передадите карту в функции и измените содержание карты, то изменения останутся для вызывающего.\nКарты могут быть созданы с использованием синтаксиса составных литералов с разделением по колонкам пар ключ-значение, поэтому легко создать начальные данные.\n\n```golang\nvar timeZone = map[string]int{\n    \"UTC\":  0*60*60,\n    \"EST\": -5*60*60,\n    \"CST\": -6*60*60,\n    \"MST\": -7*60*60,\n    \"PST\": -8*60*60,\n}\n```\n\nДобавление и получение значений из карт, синтаксически, выглядит как для массивов или срезов, за тем исключением того что индекс не обязательно должен быть целым числом.\n\n```golang\noffset := timeZone[\"EST\"]\n```\n\nПри попытке получения значения из карты по ключу, которого нет в карте, приведёт к возвращению нулевого значения.\nК примеру, если карта содержит целые числа, как описывалось выше, для несуществующего ключа будет возвращено `0`.\nЭто можно представить как карту у которой в качестве типа значения используется `bool`. Добавление записи в карту это как добавление со значением `true` в карту и дальнейшая простая проверка на индексирование.\n\n```golang\nattended := map[string]bool{\n    \"Ann\": true,\n    \"Joe\": true,\n    ...\n}\n\nif attended[person] { // will be false if person is not in the map\n    fmt.Println(person, \"was at the meeting\")\n}\n```\n\nИногда необходимо отличать отсутствие записи от нулевого значения. К примеру, есть ли запись для `\"UTC\"` или это пустая строка потому что отсутствует значение в карте?\nДля того чтобы отличить - Вы можете использовать множественное присвоение.\n\n```golang\nvar seconds int\nvar ok bool\nseconds, ok = timeZone[tz]\n```\n\nОчевидная причина называть данную идиому \"запятая ок\".\nВ данном примере, если `tz` существует, то `seconds` будет иметь необходимое значение и `ok` будет `true`, но если не существует, то `seconds` будет иметь нулевое значение а `ok` будет `false`.\nВ следующем примере, представлена функция с хорошим описанием ошибки:\n\n```golang\nfunc offset(tz string) int {\n    if seconds, ok := timeZone[tz]; ok {\n        return seconds\n    }\n    log.Println(\"unknown time zone:\", tz)\n    return 0\n}\n```\n\nВ случаи, если нас не интересует само значение, а лишь его наличие, то можно использовать **пустой идентификатор `_`**, расположенный вместо значения.\n\n```golang\n_ , present := timeZone[tz]\n```\n\nДля удаления записи из карты, необходимо использовать встроенную функцию `delete`, где в качестве аргументов задаётся карта и ключ для удаления.\nДанная операция безопасна, даже если данного ключа уже нет в карте.\n\n```golang\ndelete(timeZone, \"PDT\")  // Now on Standard Time\n```\n\n[^](#Оглавление)\n\n### Печать(Printing)\n\n[^](#Оглавление)\n\nФорматированная печать в Go подобна стилю в языке C `printf`, но более богаче и более обобщенное. Необходимые функции расположены в пакете `fmt` и имеют названия с большой буквы: `fmt.Printf`, `fmt.Fprintf`, `fmt.Sprintf` и так далее.  Функции (`Sprintf` и другие) возвращают строку, а не заполняют предоставленный буфер.\n\nВам нет необходимости в создании форматировании строк, так как для каждой  `Printf`, `Fprintf` and `Sprintf` есть пара функций к примеру `Print` и `Println`.\n\nДанные функции не берут формат строки, а вместо этого устанавливают форматирование по умолчанию для каждого аргумента. Функция `Println` также добавляет пробел между аргументами и добавляет разрыв строки в конце строки. Функция `Print` добавляет пробел только той же строке.\nВ примере каждая строка производит одинаковый результат.\n\n```golang\nfmt.Printf(\"Hello %d\\n\", 23)\nfmt.Fprint(os.Stdout, \"Hello \", 23, \"\\n\")\nfmt.Println(\"Hello\", 23)\nfmt.Println(fmt.Sprint(\"Hello \", 23))\n```\n\nДля форматированной печати функцией `fmt.Fprint` и его друзьями, принимают в качестве первого аргумента объект реализующий интерфейс `io.Writer`.\nЗначения `os.Stdout` и `os.Stderr` знакомы.\n\n\nСледующее расходится с реализацией на языке С. Первое, числовые форматы `%d` не имеют флагов знаковости или размера; Вместо этого, функции печати используют тип аргумента для задания свойств.\n\n```golang\nvar x uint64 = 1\u003c\u003c64 - 1\nfmt.Printf(\"%d %x; %d %x\\n\", x, x, int64(x), int64(x))\n```\n\nпечатает\n\n```\n18446744073709551615 ffffffffffffffff; -1 -1\n```\n\nЕсли вы используете соглашение по умолчанию, то для целых чисел можно использовать обобщенный формат `%v` (для \"значений\"); и результат будет одинаков как для  `Print` так и для `Println`.\n\nБолее того, данный формат может напечатать *любое* значение, даже срез, структуру или карту.\nПечать карты временной зоны из предыдущего раздела.\n\n```golang\nfmt.Printf(\"%v\\n\", timeZone)  // or just fmt.Println(timeZone)\n```\n\nкоторый печатает следующий результат\n\n```\nmap[CST:-21600 PST:-28800 EST:-18000 UTC:0 MST:-25200]\n```\n\nКлючи карт могут быть напечатаны в любом порядке.\nПри печати структуры, с аннотацией `%+v` производиться печать полей структуры с их именами и для каждого значения с форматом `%#v` печатается значение с полным синтаксисом Go.\n\n```golang\ntype T struct {\n    a int\n    b float64\n    c string\n}\nt := \u0026T{ 7, -2.35, \"abc\\tdef\" }\nfmt.Printf(\"%v\\n\", t)\nfmt.Printf(\"%+v\\n\", t)\nfmt.Printf(\"%#v\\n\", t)\nfmt.Printf(\"%#v\\n\", timeZone)\n```\n\nпечатает\n\n```\n\u0026{7 -2.35 abc   def}\n\u0026{a:7 b:-2.35 c:abc     def}\n\u0026main.T{a:7, b:-2.35, c:\"abc\\tdef\"}\nmap[string] int{\"CST\":-21600, \"PST\":-28800, \"EST\":-18000, \"UTC\":0, \"MST\":-25200}\n```\n\n(На заметку: обратите внимание на амперсанды)\n\nДля ссылок на строки подходит `%q`, который принимает значение на `string` или `[]byte`.\nАльтернативный формат `%#q` будет использовать обратные кавычки, если это возможно.\n(Формат `%q` также допустим для целых чисел и рун, создавая односсылочные константы рун.)\nТакже, `%x` работает со строками, массивом байт и срезом байт также как с целыми числами, создаёт шестнадцатеричные целые строки, а с пробелом в формате (`% x`) добавляет пробелы между байтами.\n\n\nДругой удобный формат `%T`, который печатает *тип* значения.\n\n```golang\nfmt.Printf(\"%T\\n\", timeZone)\n```\n\nпечатает\n\n```\nmap[string] int\n```\n\nЕсли Вы хотите свой собственный формат типа, то для этого достаточно метод с сигнатурой `String() string` для Вашего типа.\nДля нашего простого примера, тип `T`, выглядит следующим образом.\n\n```golang\nfunc (t * T) String() string {\n    return fmt.Sprintf(\"%d/%g/%q\", t.a, t.b, t.c)\n}\nfmt.Printf(\"%v\\n\", t)\n```\n\nПечатает в следующем формате\n\n```\n7/-2.35/\"abc\\tdef\"\n```\n\n(Если Вам необходимо напечатать *значение* типа `T` как указателя на тип `T`, то метод `String` должен иметь значение типа; этот пример использует указатель, т.к. они более эффективны и идиоматичны типу структуры.)\n\n\nНаша функция `String` может вызывать `Sprintf`, потому что функция печати возвращаемая и поэтому можно её обернуть. Это важно для понимания данного подхода.\nОднако, не создавайте функцию `String` вызывающую метод `Sprintf`, в случаи если далее будет рекурсивно вызвана `String`.\nЭто может произойти если `Sprintf` вызывает на печать строку получателя, который вызовет функцию снова. Эту ошибку можно легко создать и она показана на следующем примере.\n\n```golang\ntype MyString string\n\nfunc (m MyString) String() string {\n    return fmt.Sprintf(\"MyString=%s\", m) // Error: will recur forever.\n}\n```\n\nДля того чтобы решить эту проблему, необходимо изменить аргумент на базовый тип, который не имеет функции.\n\n```golang\ntype MyString string\nfunc (m MyString) String() string {\n    return fmt.Sprintf(\"MyString=%s\", string(m)) // OK: note conversion.\n}\n```\n\nДругой способ печати это допустить печать функции аргументов напрямую в другую функцию.\nСигнатура `Printf` используется для типов `...interface{}`, что допускает произвольное число аргументов, которые добавляются после формата *format*.\n\n```golang\nfunc Printf(format string, v ...interface{}) (n int, err error) {\n```\n\n**TODO**\nWithin the function `Printf`, `v` acts like a variable of type `[]interface{}` but if it is passed to another variadic function, it acts like a regular list of arguments.\nHere is the implementation of the function `log.Println` we used above. It passes  its arguments directly to `fmt.Sprintln` for the actual formatting.\n**-**\n\n```golang\n// Println prints to the standard logger in the manner of fmt.Println.\nfunc Println(v ...interface{}) {\n    std.Output(2, fmt.Sprintln(v...))  // Output takes parameters (int, string)\n}\n```\n\nЗапись `...` после `v` при вызове функции `Sprintln` объявляет компилятору о том что `v` является списком аргументов; с другой стороны `v` воспринимается как простой срез аргументов.\n\nЕсли Вам необходимо большее количество информации, то смотрите документацию `godoc` в пакете `fmt`.\n\nКстати параметр `...` может иметь тип, для примера`...int` для функции определения минимума используется список целых чисел:\n\n```golang\nfunc Min(a ...int) int {\n    min := int(^uint(0) \u003e\u003e 1)  // largest int\n    for _ , i := range a {\n        if i \u003c min {\n            min = i\n        }\n    }\n    return min\n}\n```\n\n[^](#Оглавление)\n\n### Присоединение(Append)\n\n[^](#Оглавление)\n\nВ настоящий момент? пришел момент для разъяснения конструкции встроенной функции `append`. Сигнатура функции `append` отличается от ранее описанной функции `Append`.\nСхематично, выглядит следующим образом:\n\n```golang\nfunc append(slice []*T*, elements ...*T*) []*T*\n```\n\nгде *T* любой тип. Вы не можете написать в языке Go функцию в которой `T` определена вызывающим. Поэтому необходима поддержка компилятора для функции `append`.\n\nДанная функция `append` добавляет элемент в конец среза и возвращает результат.\nПричина возврата результата, в том что как и в рукописной функции `Append` массив может измениться.\nПростой пример:\n\n```golang\nx := []int{1,2,3}\nx = append(x, 4, 5, 6)\nfmt.Println(x)\n```\n\nпечатает `[1 2 3 4 5 6]`.  Итак, `append` работает в принципе как `Printf` с произвольным количеством аргументов.\n\n\nНо что если необходимо добавить срез в срез, как в нашей реализации `Append`? Все просто: используем `...` который мы использовали в `Output`. Вот пример кода для получение того же результата.\n\n```golang\nx := []int{1,2,3}\ny := []int{4,5,6}\nx = append(x, y...)\nfmt.Println(x)\n```\n\nОбращаю внимание, что без `...` компилятор напишет ошибку, так как `y` не имеет тип `int`.\n\n\n[^](#Оглавление)\n\n## Инициализация(Initialization)\n\n[^](#Оглавление)\n\nИнициализация в языке Go более мощный инструмент нежели в языках С или С++.\nДаже сложные структуры можно инициализировать. Упорядочивание между инициализируемыми объектами разных пакетов, обрабатывается корректно.\n\n[^](#Оглавление)\n\n### Константы(Constants)\n\n[^](#Оглавление)\n\n\nКонстанты в Go это просто константы.\nОни создаются во время компиляции даже если она определена в локальной функции и могут быть цифры, символы(руны), строки или булевый тип.\nИз-за ограничения времени компиляции, компилятор должен определять какие выражения могут быть константами. К примеру, выражение `1\u003c\u003c3`  это константное выражение, в то время как выражение `math.Sin(math.Pi/4)` не является константой, так как вызывает функцию `math.Sin` требующую выполнения по время выполнения.\n\n\nВ языке Go, перечисление констант производиться с помощью перечислителя **`iota`**. Так как `iota` может быть неявно повторяемой для выражения или выражений, то легко можно строить сложные наборы значений.\n\n\n```golang\n//{{code \"/doc/progs/eff_bytesize.go\" `/^type ByteSize/` `/^\\)/`}}\n// Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage main\n\nimport \"fmt\"\n\ntype ByteSize float64\n\nconst (\n\t_           = iota // ignore first value by assigning to blank identifier\n\tKB ByteSize = 1 \u003c\u003c (10 * iota)\n\tMB\n\tGB\n\tTB\n\tPB\n\tEB\n\tZB\n\tYB\n)\n```\n\nИспользование функции `String` к пользовательским типам производить печать необходимым образом.\n**TODO**\nAlthough you'll see it most often applied to structs, this technique is also useful for scalar types such as floating-point types like `ByteSize`.\n**-**\n\n```golang\n//See code \"/doc/progs/eff_bytesize.go\"\n\nfunc (b ByteSize) String() string {\n\tswitch {\n\tcase b \u003e= YB:\n\t\treturn fmt.Sprintf(\"%.2fYB\", b/YB)\n\tcase b \u003e= ZB:\n\t\treturn fmt.Sprintf(\"%.2fZB\", b/ZB)\n\tcase b \u003e= EB:\n\t\treturn fmt.Sprintf(\"%.2fEB\", b/EB)\n\tcase b \u003e= PB:\n\t\treturn fmt.Sprintf(\"%.2fPB\", b/PB)\n\tcase b \u003e= TB:\n\t\treturn fmt.Sprintf(\"%.2fTB\", b/TB)\n\tcase b \u003e= GB:\n\t\treturn fmt.Sprintf(\"%.2fGB\", b/GB)\n\tcase b \u003e= MB:\n\t\treturn fmt.Sprintf(\"%.2fMB\", b/MB)\n\tcase b \u003e= KB:\n\t\treturn fmt.Sprintf(\"%.2fKB\", b/KB)\n\t}\n\treturn fmt.Sprintf(\"%.2fB\", b)\n}\n```\n\nВыражение `YB` печатается как `1.00YB`, когда `ByteSize(1e13)` печатает как `9.09TB`.\n\n\nИспользуемый здесь `Sprintf` в функции `String` типа `ByteSize` безопасна(не вызывается рекурсивно), не потому что происходит конвертирование, а потому что вызывается функция `Sprintf` с `%f`, который не строковый формат:`Sprintf` будет вызывать функцию `String`, функцию которой необходима строка и `%f` число с плавающей точкой.\n\n\n[^](#Оглавление)\n\n### Переменные(Variables)\n\n[^](#Оглавление)\n\nПеременные могут инициализироваться как константы, но инициализация производиться во время работы.\n\n```golang\nvar (\n    home   = os.Getenv(\"HOME\")\n    user   = os.Getenv(\"USER\")\n    gopath = os.Getenv(\"GOPATH\")\n)\n```\n\n[^](#Оглавление)\n\n### Функция init\n\n[^](#Оглавление)\n\nКаждый исходный код может определить свою первичную функцию `init` для обязательных  настройки. (На самом деле файл может иметь несколько функций `init`.)\nФункция `init` вызывается после всех объявлений переменных и после всех объявлений переменных всех пакетов.\n\nОбщее применение функции `init` в проверки или починки состояния программы до начала реального исполнения.\n\n```golang\nfunc init() {\n    if user == \"\" {\n        log.Fatal(\"$USER not set\")\n    }\n    if home == \"\" {\n        home = \"/home/\" + user\n    }\n    if gopath == \"\" {\n        gopath = home + \"/go\"\n    }\n    // gopath may be overridden by --gopath flag on command line.\n    flag.StringVar(\u0026gopath, \"gopath\", gopath, \"override default GOPATH\")\n}\n```\n\n[^](#Оглавление)\n\n## Методы(Methods)\n\n[^](#Оглавление)\n\n### Указатели или Значения\n\n[^](#Оглавление)\n\nКак мы видели в примеры с `ByteSize`, функции может иметь имя типа (кроме указателей или интерфейсов) и приемник не обязательно должен иметь структуры.\n\nКак обсуждалось ранее в срезах, мы написали функцию `Append`.\nМы можем определить функции вместе со срезом. Для этого, мы объявим именованный тип, который мы можем связать с функцией и там самым создать получателя данной функции для значений этого типа.\n\n```golang\ntype ByteSlice []byte\n\nfunc (slice ByteSlice) Append(data []byte) []byte {\n    // Body exactly the same as the Append function defined above.\n}\n```\n\nДанный метод все также возвращает обновленный срез. Для решения этой неуклюжести можно воспользоваться *указателем* на `ByteSize` в получатель, итак можно переписать следующим образом:\n\n```golang\nfunc (p *ByteSlice) Append(data []byte) {\n    slice := *p\n    // Body as above, without the return.\n    *p = slice\n}\n```\n\nНа самом деле, мы можем сделать это ещё лучше. Если мы изменим функцию, то она будет выглядеть как стандартная функция `Write`, то есть вот так,\n\n```golang\nfunc (p *ByteSlice) Write(data []byte) (n int, err error) {\n    slice := *p\n    // Again as above.\n    *p = slice\n    return len(data), nil\n}\n```\n\nтип `*ByteSlice` удовлетворяет стандартному интерфейсу `io.Writer`, что удобно. Например, мы можем напечатать один из них:\n\n```golang\n    var b ByteSlice\n    fmt.Fprintf(\u0026b, \"This hour has %d days\\n\", 7)\n```\n\nМы передаем адрес `ByteSlice`, поскольку только `*ByteSlice` удовлетворяет интерфейсу `io.Writer`.\nПравило получателя *о указателях или значениях*, в том что функции значения могут использоваться для указателей и значений, а функция указателя может только использовать указатель.\n\n\nЭто правило возникло потому что функции указателя могут изменять получателя.\nВызывая значение в функции значений получаешь копию значения, поэтому никаких модификаций не произойдет.\nПоэтому язык запрещает эту ошибку.\nКогда адресуется значение, то язык заботится о подставлении символа адресации автоматически.\n\nК примеру, переменная `b` адресованная, поэтому мы можем вызвать функцию `Write` просто вызвав `b.Write`.\nКомпилятор сам допишет `(\u0026b).Write` за нас.\n\n\nКстати, идея использования `Write` на срезах байт наиважнейшая для реализации `bytes.Buffer`.\n\n\n[^](#Оглавление)\n\n## Интерфейсы и другие типы\n\n[^](#Оглавление)\n\n### Интерфейсы\n\n[^](#Оглавление)\n\nИнтерфейсы в Go позволяют создать особое поведения для объектов: *Если нечто может делать* **это** *, то это можно использовать* **здесь**. Мы уже это встречали в простых примерах, когда реализовывали функцию `String` для печати, в то время как `Fprintf` может выдавать на печать другое с методом `Write`.\nИнтерфейсы с одним или двумя функциями свойственны в языке Go, как `io.Writer` реализующий `Write`.\n\nЛюбой тип может реализовывать множество интерфейсов.\nК примеру, коллекции могут быть отсортированы с помощью функций из пакета `sort`, если она реализует `sort.Interface`, который состоит из `Len()`, `Less(i, j int) bool`, и `Swap(i, j int)` и это может задать собственный формат.\nРассмотрим пример `Sequence`\n\n```golang\n//{{code \"/doc/progs/eff_sequence.go\" `/^type/` \"$\"}}\n// Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"sort\"\n)\n\nfunc main() {\n\tseq := Sequence{6, 2, -1, 44, 16}\n\tsort.Sort(seq)\n\tfmt.Println(seq)\n}\n\ntype Sequence []int\n\n// Methods required by sort.Interface.\nfunc (s Sequence) Len() int {\n\treturn len(s)\n}\nfunc (s Sequence) Less(i, j int) bool {\n\treturn s[i] \u003c s[j]\n}\nfunc (s Sequence) Swap(i, j int) {\n\ts[i], s[j] = s[j], s[i]\n}\n\n// Method for printing - sorts the elements before printing.\nfunc (s Sequence) String() string {\n\tsort.Sort(s)\n\tstr := \"[\"\n\tfor i, elem := range s {\n\t\tif i \u003e 0 {\n\t\t\tstr += \" \"\n\t\t}\n\t\tstr += fmt.Sprint(elem)\n\t}\n\treturn str + \"]\"\n}\n```\n\n[^](#Оглавление)\n\n### Преобразование (Conversions)\n\n[^](#Оглавление)\n\nФункция `String` работает с `Sequence` и `Sprint` уже работает со срезами. Мы может распространить данный эффект, если конвертируем  `Sequence` на  `[]int` до вызова `Sprint`.\n\n```golang\nfunc (s Sequence) String() string {\n    sort.Sort(s)\n    return fmt.Sprint([]int(s))\n}\n```\n\nЭто функция другой пример техники конвертирования для вызова `Sprintf` безопасно для функции `String`.\nТак как два типа (`Sequence` и `[]int`) одинаковы, то мы можем игнорировать имя типа, это допустимое конвертирование между ними.\nПри конвертации не происходит создание нового значения, это временная замена существующего значения на новый тип.\n(При других допустимых конвертациях, к примеру из целого числа в число с плавающей точкой, происходит создание нового значения.)\n\n\nЭто идиоматично в программе Go - конвертация типа позволяет получить доступ к другим функциям. К примеру, мы можем использовать существующий тип `sort.IntSlice`:\n\n```golang\ntype Sequence []int\n\n// Method for printing - sorts the elements before printing\nfunc (s Sequence) String() string {\n    sort.IntSlice(s).Sort()\n    return fmt.Sprint([]int(s))\n}\n```\n\nТеперь, наш `Sequence` реализует множество интерфейсов (сортировка и печать),  мы можем использовать множество типов (`Sequence`, `sort.IntSlice` и `[]int`), которые выполняют определенную часть работ.\nЭто не типично в использовании, но эффективно.\n\n[^](#Оглавление)\n\n### Конвертация интерфейсов и привязка типов\n\n[^](#Оглавление)\n\nПереключатель типов(Type switches) является одной из форм конвертации: на основе интерфейса и переключателя для каждого элемента, в некотором смысле преобразует тип в элемент переключателя.\nЭто простой вариант как в коде `fmt.Printf` конвертирует значение в строку, используя переключатель типа.\nИ если это уже строка, мы хотим чтобы фактическое значение происходило по его интерфейсу, но в случаи если она имеет функцию `String`, то хотим чтобы в результате вызывалась именно она.\n\n```golang\ntype Stringer interface {\n    String() string\n}\n\nvar value interface{} // Value provided by caller.\nswitch str := value.(type) {\ncase string:\n    return str\ncase Stringer:\n    return str.String()\n}\n```\n\nВ первом случае ищется конкретное значение, во втором случаи происходит преобразование интерфейса в другой интерфейс.\nЭто хороший подход в преобразовании типов.\n\nЧто если, мы будем беспокоиться лишь об одном типе? Если мы","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FKonstantin8105%2FEffective_Go_RU","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FKonstantin8105%2FEffective_Go_RU","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FKonstantin8105%2FEffective_Go_RU/lists"}