An open API service indexing awesome lists of open source software.

https://github.com/Konstantin8105/Effective_Go_RU

Перевод - Эффективный Go
https://github.com/Konstantin8105/Effective_Go_RU

documentation effective-go go golang

Last synced: about 1 year ago
JSON representation

Перевод - Эффективный Go

Awesome Lists containing this project

README

          

# Effective Go (RU) (Эффективный Go)

------

> ⚠️ **Внимание!**
> Данный перевод не завершен и устарел, [смотри новый здесь в .md или .pdf формате](https://github.com/0x0FACED/effective-go-ru)

Оригинал смотри: https://golang.org/doc/effective_go.html
`go version go1.7.4`

------

**Список дополнительных материалов:**
* [Руководство сотрудничества](https://github.com/Konstantin8105/Contribution_Guide_RU)
* [Эффективный Go (Свежий перевод)](https://github.com/0x0FACED/effective-go-ru)
* [Эффективный Go](https://github.com/Konstantin8105/Effective_Go_RU)

------
# Оглавление

- [Effective Go (RU) (Эффективный Go)](#effective-go-ru-эффективный-go)
- [Оглавление](#оглавление)
- [Введение](#введение)
- [Примеры](#примеры)
- [Форматирование](#форматирование)
- [Абзац](#абзац)
- [Длина строки](#длина-строки)
- [Круглые скобки](#круглые-скобки)
- [Комментарии](#комментарии)
- [Именование](#именование)
- [Именование пакетов](#именование-пакетов)
- [Геттеры](#геттеры)
- [Имена интерфейсов](#имена-интерфейсов)
- [MixedCaps](#mixedcaps)
- [Точка с запятой](#точка-с-запятой)
- [Управляющие структуры](#управляющие-структуры)
- [If](#if)
- [Переопределение и переприсваивание](#переопределение-и-переприсваивание)
- [Оператор For](#оператор-for)
- [Switch(переключатель)](#switchпереключатель)
- [Переключатель типов (Типизированный переключатель, Type switch)](#переключатель-типов-типизированный-переключатель-type-switch)
- [Функции и методы(Functions, методы)](#функции-и-методыfunctions-методы)
- [Множественное возвращение результатов](#множественное-возвращение-результатов)
- [Именование параметров результата](#именование-параметров-результата)
- [Отсроченный вызов (Defer)](#отсроченный-вызов-defer)
- [Данные](#данные)
- [Созданные с помощью `new`](#созданные-с-помощью-new)
- [Конструкторы и составные литералы](#конструкторы-и-составные-литералы)
- [Создание с помощью `make`](#создание-с-помощью-make)
- [Массивы](#массивы)
- [Срезы(Slices, слайсы)](#срезыslices-слайсы)
- [Двухмерные срезы](#двухмерные-срезы)
- [Карты(Maps)](#картыmaps)
- [Печать(Printing)](#печатьprinting)
- [Присоединение(Append)](#присоединениеappend)
- [Инициализация(Initialization)](#инициализацияinitialization)
- [Константы(Constants)](#константыconstants)
- [Переменные(Variables)](#переменныеvariables)
- [Функция init](#функция-init)
- [Методы(Methods)](#методыmethods)
- [Указатели или Значения](#указатели-или-значения)
- [Интерфейсы и другие типы](#интерфейсы-и-другие-типы)
- [Интерфейсы](#интерфейсы)
- [Преобразование (Conversions)](#преобразование-conversions)
- [Конвертация интерфейсов и привязка типов](#конвертация-интерфейсов-и-привязка-типов)
- [Общее(Generality)](#общееgenerality)
- [Интерфейсы и методы(функции)](#интерфейсы-и-методыфункции)
- [Пустой идентификатор (The blank identifier **\_**)](#пустой-идентификатор-the-blank-identifier-_)
- [Пустой идентификатор в множественном присваивании (**\_**)](#пустой-идентификатор-в-множественном-присваивании-_)
- [Неиспользуемое импортирование и значения](#неиспользуемое-импортирование-и-значения)
- [Импортирование для побочного эффекта (Import for side effect)](#импортирование-для-побочного-эффекта-import-for-side-effect)
- [Проверка интерфейса (Interface checks)](#проверка-интерфейса-interface-checks)
- [Вложение (Embedding)](#вложение-embedding)
- [Согласованность, параллельная обработка, параллельное выполнение (Concurrency)](#согласованность-параллельная-обработка-параллельное-выполнение--concurrency)
- [Распределение памяти по сообщениям (Share by communicating)](#распределение-памяти-по-сообщениям-share-by-communicating)
- [Го-рутины (Goroutines)](#го-рутины-goroutines)
- [Каналы (Channels)](#каналы-channels)
- [Канал каналов (Channels of channels)](#канал-каналов-channels-of-channels)
- [Параллелизм (Parallelization)](#параллелизм-parallelization)
- [Текущий буфер (A leaky buffer)](#текущий-буфер-a-leaky-buffer)
- [Ошибки (Errors)](#ошибки-errors)
- [Паника (Panic)](#паника-panic)
- [Восстановление (Recover)](#восстановление-recover)
- [Веб-сервер](#веб-сервер)

--------------

## Введение

[^](#Оглавление)

Go - это новый язык программирования. Хотя, он заимствует идеи из существующих языков, он обладает необычными свойствами, которые позволяют создавать эффективные программы, язык Go отличается по своему характеру от программ, написанных на родственных языках. Прямолинейный перевод C++ или Java программ в Go вряд ли даст удовлетворительный результат, т.к. Java программы написаны на Java, не на Go. С другой стороны, думая о проблеме с точки зрения Go можно добиться успеха, но это уже другая программа. Другими словами, для хорошего написания кода на языке Go, важно понимать его особенности и идиомы.
Также важно знать установленные соглашения для программирования на Go, такие как именование, форматирование, разработка программ и так далее, так чтобы программы написанные Вами были простыми для понимания другими программистами Go.

Этот документ даёт примеры для написания чистого, идеоматичного кода на Go.
Он дополняет [спецификацию языка](https://golang.org/ref/spec), [Тур по Go](https://tour.golang.org/),
и [Как писать на Go](https://golang.org/doc/code.html), каждую из которых необходимо прочитать в первую очередь.

[^](#Оглавление)

### Примеры

[^](#Оглавление)

[Go пакеты исходных кодов](https://golang.org/src/) предназначены не только в качестве основных библиотек, но и в качестве примеров использования языка.
Кроме того, многие пакеты имеют работающие, автономные исполняемые примеры и Вы можете запустить напрямую с помощью страницы [golang.org](https://golang.org), такие как [этот](https://golang.org/pkg/strings/#example_Map) (если необходимо, нажмите на слово "Примеры" чтобы открыть их).
Если у Вас есть вопрос о том как решить какую-либо проблему или как что-то реализовать, то документация, исходные коды и примеры в библиотеке могут дать ответ, идею или объяснение.

[^](#Оглавление)

## Форматирование

[^](#Оглавление)

Форматирование является наиболее спорным, но не сильно важным вопросом.
Люди могут привыкнуть к различным стилям форматирования, но было бы лучше, если бы этого не приходилось делать и меньше времени придавалось этой теме, если бы все использовали одинаковый стиль.
Проблема данной утопии в том, как это сделать без длинного руководства по стилю.

В Go мы используем нетипичный подход и передаем машине заботу о форматировании.
Программа `gofmt` (также доступна, как `go fmt`, которая производит действия на уровне пакета, а не на уровне файлов) читает код на Go и выпускает исходный код со стандартным стилем отступов и вертикальным выравниванием, сохраняет, и при необходимости, переформатирует комментарии.
Если Вы хотите знать, как по-новому структурировать код, запустите `gofmt`; если структура неверна, `gofmt` поправит Вашу программу (или файл сообщит об ошибке `gofmt`), не работайте в обход форматирования программой `gofmt`.

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

```golang
type T struct {
name string // name of the object
value int // its value
}
```

`gofmt` произведет выравнивание по колонкам:

```golang
type T struct {
name string // name of the object
value int // its value
}
```

Все стандартные пакеты Go отформатированы с помощью `gofmt`.

Очень коротко о некоторых деталях форматирования:

#### Абзац
Мы используем табуляцию для абзацев и `gofmt` делает это по умолчанию. Используйте пробелы только при острой необходимости.
#### Длина строки
Go не имеет предела длины строки. Не беспокойтесь о длинных строках. Если строка кажется слишком длинной, прервите ее и добавьте дополнительный отступ (символ табуляции) на новой строке.
#### Круглые скобки
Go нуждается в меньшем количестве круглых скобок, чем C и Java: структуры ветвления, цикла ( `if` , `for` , `switch`) не имеют круглых скобок в своём синтаксисе. Также, иерархия операторов стала проще и короче. К примеру, выражение

```
x<<8 + y<<16
```
не нуждается в добавлении пробелов, в отличии от других языков.

[^](#Оглавление)

## Комментарии

[^](#Оглавление)

Go использует C-стиль `/* */` для блока комментариев и C++-стиль `//` для однострочных комментариев.
Как правило, используются однострочные комментарии. Блок комментариев, в основном, используется при комментировании пакетов,
но также для выразительности или отключения большого участка кода.

Программа и веб-сервер - `godoc` обрабатывает Go исходники пакета для формирования документации.
Комментарии, расположенные сразу над объявлением (без дополнительных пустых строк), извлекаются вместе с объявлением для пояснения данного элемента.
Характер и стиль комментариев напрямую влияет на качество документации производимой `godoc`.

Каждый пакет должен иметь *комментарий пакета* - это блок комментариев предшествующий объявлению пакета.
Для пакетов состоящих из нескольких файлов, комментарий пакета может быть расположен в любом из файлов, но только в одном из них.
Комментарий пакета должен представлять информацию о пакете в целом.
Он будет отображен вначале страницы `godoc` и должен представлять из себя детальную информацию, которой можно пользоваться.

```golang
/*
Package regexp implements a simple library for regular expressions.

The syntax of the regular expressions accepted is:

regexp:
concatenation { '|' concatenation }
concatenation:
{ closure }
closure:
term [ '*' | '+' | '?' ]
term:
'^'
'$'
'.'
character
'[' [ '^' ] character-ranges ']'
'(' regexp ')'
*/
package regexp
```

Если пакет простой, то комментарий может быть кратким.

```golang
// Package path implements utility routines for
// manipulating slash-separated filename paths.
```

Дополнительное форматирование, к примеру баннер из * (звездочек), не требуется.
Шрифт для сформированного результата не обязательно будет моноширинный, поэтому не полагайтесь на пробелы при выравнивании, `godoc`, также как `gofmt`, позаботятся об этом.
Комментарии интерпретируются как простой текст, поэтому HTML и другие аннотации такие как `_эта_` воспроизводятся *дословно* и поэтому не должны использоваться. Единственное исключение,
которое делает `godoc`, это выделение моноширинным шрифтом участков кода с отступами.
Хорошим примером такого исключения является комментарий к пакету [`fmt`](https://golang.org/pkg/fmt/).

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

Любые комментарии внутри пакета, предшествующие объявлению, используются как описание этого объявления.
Каждый экспортируемый объект, название которого начинается с большой буквы, должен иметь комментарий.

Лучше всего использовать комментарии в виде полных предложений. Это позволяет производить их автоматическую обработку.
Первое предложение должно быть ключевым и начинаться с имени объявления.

```golang
// Compile parses a regular expression and returns, if successful,
// a Regexp that can be used to match against text.
func Compile(str string) (*Regexp, error) {
```

Если комментарий начинается с имени, то `godoc` может с использоваться совместно с `grep`.
Представьте, что Вы не можете вспомнить имя "Compile", но Вы ищите *the parsing function* для регулярных выражений и тогда Вы можете выполнить команду:

```command
$ godoc regexp | grep -i parse
```

Если все комментарии в пакете начинаются с "This function...", `grep` не сможет помочь с поиском имени.
Если же комментарии начинаются с имени, Вы можете увидеть что-то вроде следующего результата, который напомнит Вам о том, что Вы искали.

```command
$ godoc regexp | grep parse
Compile parses a regular expression and returns, if successful, a Regexp
parsed. It simplifies safe initialization of global variables holding
cannot be parsed. It simplifies safe initialization of global variables
$
```

Синтаксис Go допускает групповое объявление. Для каждой группы констант или переменных может быть представлен один общий комментарий. Однако такое объявление выглядит небрежно.

```golang
// Error codes returned by failures to parse an expression.
var (
ErrInternal = errors.New("regexp: internal error")
ErrUnmatchedLpar = errors.New("regexp: unmatched '('")
ErrUnmatchedRpar = errors.New("regexp: unmatched ')'")
...
)
```

Группировка также может показать взаимосвязи между элементами, к примеру, группа переменных защищенных mutex:

```golang
var (
countLock sync.Mutex
inputCount uint32
outputCount uint32
errorCount uint32
)
```

[^](#Оглавление)

## Именование

[^](#Оглавление)

Именование очень важно в Go, как и в других языках.
Они имеют семантический эффект: **Видимость имени за пределами пакета, определяется по первой букве имени, которая, если является заглавной, то имя будет видно вне это пакета**.
Именно поэтому стоит уделить время обсуждению соглашения об именовании в программах Go.

### Именование пакетов

[^](#Оглавление)

Когда пакет импортируется, имя пакета используется для доступа к его содержимому.
После того, как пакет импортирован,

```golang
import "bytes"
```

можно использовать `bytes.Buffer`. Это полезно, если все, кто использует пакет, могут использовать одно и то же имя, для обращения к его содержимому, подразумевается, что имя пакета должно быть коротким, четким и запоминающимся. В соответствии с соглашением,имена пакетов состоят из одного слова в нижнем регистре; нет необходимости в использовании подчеркиваний или СмешанногоРегистра. При выборе длинного имени пакета, всем, кто будет его использовать, придётся писать это имя. Но не беспокойтесь об уникальности имени.
Имя пакета только по умолчанию используется при импорте; оно не должно быть глобально уникальным, и в редких случаях, при импорте может быть указано другое имя. В любом случае,
путаница встречается редко, так как имя файла в импорте определяет, какой именно пакет используется.

Согласно другому соглашению, имя пакета является базовым именем его исходного каталога; пакет `src/encoding/base64` импортируется как `"encoding/base64"` и имеет название `base64`, а не `encoding_base64` и не `encodingBase64`.

Импортирующий пакет будет использовать имя пакета для обозначения его содержимого, поэтому при экспорте может учитываться этот факт, чтобы избежать повторения.
(Не используйте `import .`, это, конечно, может упростить запуск тестов вне пакета, но в других случаях использоваться не должно). Например, тип *reader* для буферного чтения описанный в пакете `bufio` называется `Reader`, а не `BufReader`, т.к пользователи его видят как `bufio.Reader`, имя которого кратко и понятно.

Более того, т.к. импортируемые объекты адресуются по имени пакета, следовательно `bufio.Reader` не будет конфликтовать с `io.Reader`.
Аналогично, функция для создания нового экземпляра объекта `ring.Ring`, которая объявлена как *конструктор* в Go, может называться `NewRing`, но т.к. `Ring` - это экспортируемый тип из пакета `ring`, функция-конструктор может называться просто `New`, которую, можно будет вызвать как `ring.New`. Используйте структуру пакетов при
выборе имен.

Другой короткий пример функция `once.Do`; `once.Do(setup)` читается хорошо, и при этом
лучше не станет, если ее переименовать в `once.DoOrWaitUntilDone(setup)`.
Длинные имена не делают названия более читабельными. В то время как комментарии
могут быть более ценным, чем длинные имена.

## Геттеры

[^](#Оглавление)

Go не предоставляет автоматическую поддержку геттеров и сеттеров.
Но не будет ошибкой создание геттеров и сеттеров самостоятельно, и если это необходимо, то делайте так, но идиоматически нет необходимости добавлять `Get` в имя геттера.
Если у Вас есть поле с именем `owner` (с маленькой буквы, неэкспортируемое), то геттер может называться `Owner` (с большой буквы, экспортируемый), а не `GetOwner`.
Использование имен, начинающихся с заглавной буквы, позволяет отделить экспортируемые методы от неэкспортируемых полей. Cеттер, при необходимости, может быть назван `SetOwner`.
Оба примера в следующем коде:

```golang
owner := obj.Owner()
if owner != user {
obj.SetOwner(user)
}
```

### Имена интерфейсов

[^](#Оглавление)

По соглашению, интерфейсы с одним методом должны называться как метод с суффиксом `-er` или подобно этому, для образования существительного: `Reader`, `Writer`, `Formatter`, `CloseNotifier` и т.д.

Существует целый ряд имен, которыe соблюдают это соглашение и содержат подобные методы. `Read` , `Write` , `Close`, `Flush`, `String` и т.д., имеют канонические подписи и значения. Чтобы избежать путаницы, не давайте методу ни одного из этих имен, если оно не имеет ту же сигнатуру и значение. С другой стороны, если ваш тип реализует метод с тем же значением, как и метод хорошо известного типа, то дайте ему то же имя и значение; назовите Ваш метод конвертации в строку `String` , а не `ToString`.

### MixedCaps

[^](#Оглавление)

В заключении, Go соглашение использует `MixedCaps` или `mixedCaps` , а не подчеркивание для имен из нескольких слов.

### Точка с запятой

[^](#Оглавление)

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

Правило такое. Если последний токен(лексема) перед символом новой строки - идентификатор (который включает такие слова, как `int` и `float64`), базовый литерал, такой как число или строковая константа, или один из нижеперечисленных токенов

```golang
break continue fallthrough return ++ -- ) }
```

то, лексер всегда добавляет точку с запятой после него. Вкратце, это может звучать так: "Если новая строка начинается после токена, который может закрывать операцию-выражение, то добавить точку с запятой".

Точка с запятой также может быть опущена сразу перед закрывающей скобкой, таким
образом для операции-выражения такой как:

```golang
go func() { for { dst <- <-src } }()
```

точка с запятой не требуется.

Как следствие из правила, вы не можете перенести открывающую скобку управляющих
структур (`if`, `for`, `switch` или `select`) на новую строку. Если перенесете,
точка с запятой будет вставлена перед скобкой, которая может стать причиной
нежелательных эффектов. Пишите так,

```golang
if i < f() {
g()
}
```

но не так

```golang
if i < f() // ошибка!
{ // ошибка!
g()
}
```

[^](#Оглавление)

## Управляющие структуры

[^](#Оглавление)

Управляющие структуры в Go аналогичны тем же структурам в C, но имеют ряд важных отличий. Во-первых нет циклов `do` и `while`, есть лишь обобщенный `for`. Во-вторых, `switch` более гибкий. В-третьих `if` и `switch` имеют опциональную инициализацию переменных, как и в `for`. В-четвертых, `break` и `continue` опционально принимают метку, к которой необходимо перейти. В-пятых, есть новые операторы, такие как типизированный `switch` и многоканальный `select`. Синтаксис также немного отличается: отсутствуют круглые скобки в условии, и тело структуры всегда должно быть ограничено фигурными скобками.

[^](#Оглавление)

### If

[^](#Оглавление)

В Go простой `if` выглядит так:

```golang
if x > 0 {
return y
}
```

Обязательные фигурные скобки упрощают написание простых условий `if` на
несколько строк. Это хороший стиль в любом случае, особенно когда тело содержит управляющие операторы, такие как `return` или `break`.

Поскольку `if` и `switch` допускают инициализацию переменных, то часто можно
видеть подобную запись:

```golang
if err := file.Chmod(0664); err != nil {
log.Print(err)
return err
}
```

В библиотеках Go, вы найдёте подобную запись, если `if` не переходит в следующий блок, т.е. в теле используется `break`, `continue`, `goto` или `return`, а необязательный `else` опускается.

```golang
f, err := os.Open(name)
if err != nil {
return err
}
codeUsing(f)
```

В данном примере представлена общая схема, где код защищен от серии ошибок. Код читается хорошо, если выполняется без ошибок, обходя случаи их возникновения. Так как ошибки приводят к завершению выполнения блока с помощью `return`, то блок `else` не требуется.

```golang
f, err := os.Open(name)
if err != nil {
return err
}
d, err := f.Stat()
if err != nil {
f.Close()
return err
}
codeUsing(f, d)
```

[^](#Оглавление)

### Переопределение и переприсваивание

[^](#Оглавление)

Последний пример предыдущего раздела демонстрирует использование краткой формы объявления переменных `:=`. Вызов `os.Open` объявляет сразу две переменных `f` и `err`

```golang
f, err := os.Open(name)
```

Несколькими строками ниже вызывается `f.Stat`,

```golang
d, err := f.Stat()
```

который выглядит как объявления двух переменных `d` и `err`. Хотя `err` присутствует в обоих объявлениях. Это дублирование вполне законно: `err` объявляется в первом случае, и лишь переприсваивается во втором. Это означает, что `f.Stat` использует уже существующую переменную `err`, определенную выше, и просто присваивает ей новое значение.

В объявлении `:=` переменная `v` может присутствовать, даже если она уже объявлена, при условии:

* если объявление происходит в той же самой области видимости, что и существующая переменная `v` (если `v` уже объявлена за пределами видимости, то объявление создаст новую переменную §)
* соответствующее значение, при инициализации, может быть присвоено `v`
* существует хотя бы одна новая переменная в объявлении, которая будет создана заново

Это необычное свойство - чистая практичность, которая служит для упрощения
использования одной переменной `err`, к примеру, в длинных цепочках `if-else`.
Вы увидите, это используется часто.

§ Нет ничего плохого в том, что в Go область видимости параметров и возвращаемых значений функции - есть само тело функции, хотя они лексически находятся за скобками, ограничивающими тело функции.

[^](#Оглавление)

### Оператор For

[^](#Оглавление)

В Go цикл `for` очень похож, но не такой же как в C. Он унифицирует `for` и `while`, при этом отсутствует `do-while` цикл. Существует 3 различных формы, и только в одной из них используется точка с запятой.

```golang
// C-подобный for
for init; condition; post { }

// C-подобный while
for condition { }

// C-подобный for(;;)
for { }
```

Краткая запись позволяет легко объявить начальные условия прямо в цикле:

```golang
sum := 0
for i := 0; i < 10; i++ {
sum += i
}
```

Если Вы итерируетесь по массиву, срезу, строке или map'у, или читаете из канала, то для управления можно использовать `range`.

```golang
for key, value := range oldMap {
newMap[key] = value
}
```

Если необходимо использовать только первый элемент *диапазона* (ключ или индекс), отбросьте второй:

```golang
for key := range m {
if key.expired() {
delete(m, key)
}
}
```

Если вам необходим только второй элемент (значение), то используйте *пустой идентификатор* (**_**) в качестве первого элемента:

```golang
sum := 0
for _ , value := range array {
sum += value
}
```

Пустой идентификатор используется в разных случаях и будет описан позже.

Для строк, оператор `range` выполняет ещё больше работы, к примеру разделяет строку по символам Unicode в соответствии с UTF-8. При ошибочном использование кодировки, побайтово заменяет рунами(*rune*) U+FFFD. (`rune` (и одноименный встроенный тип) в терминологии Go используется для работы с символами Unicode. Смотрите детальную информацию в [Спецификации языка](https://golang.org/ref/spec#Rune_literals)).

Данный цикл:

```golang
for pos, char := range "日本\x80語" { // \x80 is an illegal UTF-8 encoding
fmt.Printf("character %#U starts at byte position %d\n", char, pos)
}
```

Выводит:

```command
character U+65E5 '日' starts at byte position 0
character U+672C '本' starts at byte position 3
character U+FFFD '�' starts at byte position 6
character U+8A9E '語' starts at byte position 7
```

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

```golang
// Reverse a
for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
a[i], a[j] = a[j], a[i]
}
```

[^](#Оглавление)

### Switch(переключатель)

[^](#Оглавление)

В языке Go `switch` более обобщён, нежели в C. Выражения не обязательно должны
быть константами или даже целыми числами, условия проверяются сверху-вниз до нахождения соответствия, и если `switch` не имеет выражений, то переходит в `true`. Следовательно, идиоматически возможно записывать `if-else-if-else` цепочку как `switch`.

```golang
func unhex(c byte) byte {
switch {
case '0' <= c && c <= '9':
return c - '0'
case 'a' <= c && c <= 'f':
return c - 'a' + 10
case 'A' <= c && c <= 'F':
return c - 'A' + 10
}
return 0
}
```

Автоматический пропуск условий отсутствует, но, при этом, условия могут быть записаны через запятую:

```golang
func shouldEscape(c byte) bool {
switch c {
case ' ', '?', '&', '=', '#', '+', '%':
return true
}
return false
}
```
Несмотря на то, что они не столь распространены в Go, как в некоторых других C-подобных языках, `break` может быть использован для досрочного прерывания `switch`.
Хотя, иногда, надо прервать внешний (по отношению к `switch`) цикл, а не сам `switch`, и в Go это может быть достигнуто путём добавления метки перед циклом, и переходом к этой метке в случае вызова `break`. В следующем примере представлены оба случая:

```golang
Loop:
for n := 0; n < len(src); n += size {
switch {
case src[n] < sizeOne:
if validateOnly {
break
}
size = 1
update(src[n])

case src[n] < sizeTwo:
if n+1 >= len(src) {
err = errShortInput
break Loop
}
if validateOnly {
break
}
size = 2
update(src[n] + src[n+1]< b
func Compare(a, b []byte) int {
for i := 0; i < len(a) && i < len(b); i++ {
switch {
case a[i] > b[i]:
return 1
case a[i] < b[i]:
return -1
}
}
switch {
case len(a) > len(b):
return 1
case len(a) < len(b):
return -1
}
return 0
}
```

[^](#Оглавление)

### Переключатель типов (Типизированный переключатель, Type switch)

[^](#Оглавление)

`switch` может быть использован для определения динамических типов интерфейсных переменных. Так, типизированный `switch` использует синтаксис приведения типов,
с ключевым словом `type` внутри скобок. Если `switch` объявляет переменную в
выражении, то переменная будет иметь соответствующий тип в каждом пункте. Также, идиоматически верно переиспользовать имена переменных для объявления новых переменных
с тем же именем, но другим типом в каждом случае:

```golang
var t interface{}
t = functionOfSomeType()
switch t := t.(type) {
default:
fmt.Printf("unexpected type %T\n", t) // %T prints whatever type t has
case bool:
fmt.Printf("boolean %t\n", t) // t has type bool
case int:
fmt.Printf("integer %d\n", t) // t has type int
case *bool:
fmt.Printf("pointer to boolean %t\n", *t) // t has type *bool
case *int:
fmt.Printf("pointer to integer %d\n", *t) // t has type *int
}
```

## Функции и методы(Functions, методы)

[^](#Оглавление)

### Множественное возвращение результатов

[^](#Оглавление)

Одно из особенностей языка Go - это то, что функции и методы могут возвращать множество значений.

При использовании языка С, передача ошибки производится через отрицательное значение с описанием причины ошибки в "другом" месте.

При использовании языка Go, функция `Write` может вернуть одновременно **и** возвращаемое значение **и** ошибку.
Сигнатура метода `Write` в файлах пакета `os`:

```golang
func (file *File) Write(b []byte) (n int, err error)
```

и как предусмотрено документацией, он возвращает число записанных байт и ненулевое значение ошибки `error`, когда `n` `!=` `len(b)`.
Это общий стиль, смотрите также раздел посвящённый ошибкам в качестве примера.

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

```golang
func nextInt(b []byte, i int) (int, int) {
for ; i < len(b) && !isDigit(b[i]); i++ {
}
x := 0
for ; i < len(b) && isDigit(b[i]); i++ {
x = x*10 + int(b[i]) - '0'
}
return x, i
}
```

Вы можете сканировать число чисел во входном срезе `b` следующим образом:

```golang
for i := 0; i < len(b); {
x, i = nextInt(b, i)
fmt.Println(x)
}
```

### Именование параметров результата

[^](#Оглавление)

Возвращаемым "параметрам" в языке Go можно давать имена и это часто используется как входные параметры.
Когда они именованы, то они инициализируются нулевым значением необходимого типа в самом начале функции.
Если функция, в которой определены именованные параметры, вызывает конструкцию возврата без аргументов, то значения именованных параметров будут использованы ей как возвращаемые значения.
Именование не обязательное, но оно может сделать код короче и чище - самодокументированным.
Если имя результата будет `nextInt`, то очевидно что тип результата `int`.

```golang
func nextInt(b []byte, pos int) (value, nextPos int) {
```

На примере `io.ReadFull`:

```golang
func ReadFull(r Reader, buf []byte) (n int, err error) {
for len(buf) > 0 && err == nil {
var nr int
nr, err = r.Read(buf)
n += nr
buf = buf[nr:]
}
return
}
```

[^](#Оглавление)

### Отсроченный вызов (Defer)

[^](#Оглавление)

В языке Go есть оператор `defer` для управления отложенного вызова функции, который будет вызван, как только функция имеющая `defer` оканчивается.
Это не типичный но эффективный способ, когда необходимо закрыть ресурс после окончания функции.
Канонические примеры - работа с mutex или закрытие файла.

```golang
// Contents returns the file's contents as a string.
func Contents(filename string) (string, error) {
f, err := os.Open(filename)
if err != nil {
return "", err
}
defer f.Close() // f.Close will run when we're finished.

var result []byte
buf := make([]byte, 100)
for {
n, err := f.Read(buf[0:])
result = append(result, buf[0:n]...) // append is discussed later.
if err != nil {
if err == io.EOF {
break
}
return "", err // f will be closed if we return here.
}
}
return string(result), nil // f will be closed if we return here.
}
```

Отложенный вызов функции `Close` имеет 2 преимущества. Во-первых, гарантирует что не будет забыто закрытие файла - ошибка, которую легко сделать, если в последствии в функции будет изменен параметр на другую папку. Во-вторых, закрытие близко расположено к открытию, что более ясно, чем располагать его в конце функции.

Аргументы отложенной функции выполняются когда выполняется `defer`, а не когда функция вызвана.
Кроме того , во избежания беспокойства по поводу изменяющихся переменных в функции, одна отложенная функция может отложить вызов множества функций.

Вот простой пример:

```golang
for i := 0; i < 5; i++ {
defer fmt.Printf("%d ", i)
}
```

Откладывание функции в LIFO очередь, приведет к следующей работе функции при печати на экран `4 3 2 1 0` . Более интересный пример - простое отслеживание функции в программе. Мы могли бы написать простое отслеживание, как это:

```golang
func trace(s string) { fmt.Println("entering:", s) }
func untrace(s string) { fmt.Println("leaving:", s) }

// Use them like this:
func a() {
trace("a")
defer untrace("a")
// do something....
}
```

Мы могли бы сделать лучше - используя факт отложенных функций для оценки когда будет запущен `defer`. Отслеживаемая функция может настроить аргументы неотслеживаемой функции.
К примеру:

```golang
func trace(s string) string {
fmt.Println("entering:", s)
return s
}

func un(s string) {
fmt.Println("leaving:", s)
}

func a() {
defer un(trace("a"))
fmt.Println("in a")
}

func b() {
defer un(trace("b"))
fmt.Println("in b")
a()
}

func main() {
b()
}
```

выводит:

```
entering: b
in b
entering: a
in a
leaving: a
leaving: b
```

Для программистов привыкших к блочному управлению ресурсами в других языках, функция `defer` может показаться странной, но интересной и мощной, так как позволяет уйти от блочного управления к управлению в функции. В разделах `panic` и `recover` будут также рассматриваться несколько примеров.

[^](#Оглавление)

## Данные

[^](#Оглавление)

### Созданные с помощью `new`

[^](#Оглавление)

Для создания примитивов в языке Go используются функции `new` и `make`.
Они разные и применяются для разных типов, это может сбить с толку, но правило очень просто.
Для начала обсудим функцию `new`.
Данная функция резервирует память, но не также как в других языках программирования, она не просто *инициализирует* память, а вместо этого заполняет *нулями*.

К примеру `new(T)` резервирует память нулями для нового элемента типа `T` и возвращает его указатель на значение типа `*T`. В терминологии Go, он возвращает указатель на новую зарезервированную память заполненная нулями с типом `T`.

**TODO**
Since 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.
For example, the documentation for `bytes.Buffer` states that "the zero value for `Buffer` is an empty buffer ready to use."
Similarly, `sync.Mutex` does not have an explicit constructor or `Init` method.
Instead, the zero value for a `sync.Mutex` is defined to be an unlocked mutex.
The zero-value-is-useful property works transitively. Consider this type declaration.
**-**

```golang
type SyncedBuffer struct {
lock sync.Mutex
buffer bytes.Buffer
}
```

**TODO**
Values 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.
**-**

```golang
p := new(SyncedBuffer) // type *SyncedBuffer
var v SyncedBuffer // type SyncedBuffer
```

### Конструкторы и составные литералы

[^](#Оглавление)

Иногда нулевых значений не достаточно и необходимо иметь конструктор, следующий пример взят из пакета `os`.

```golang
func NewFile(fd int, name string) *File {
if fd < 0 {
return nil
}
f := new(File)
f.fd = fd
f.name = name
f.dirinfo = nil
f.nepipe = 0
return f
}
```

Существует много шаблонов. Мы просто можем использовать *составные литералы*, которые будут создавать новые сущности каждый раз.

```golang
func NewFile(fd int, name string) *File {
if fd < 0 {
return nil
}
f := File{fd, name, nil, 0}
return &f
}
```

Обратите внимание на то, что в отличии от языка С, это нормально, возвращать адрес локальных переменных, так как переменная уже существует после возвращения из функции.
На самом деле, возвращение адресов составных литералов создает новую сущность каждый раз, как он вычисляется.
Итак мы можем объединить последние две строки:

```golang
return &File{fd, name, nil, 0}
```

Поля составных литералов должны быть в порядке объявления и все должны присутствовать.
Однако, используя маркировку как пара *поле*`:`*значение*, могут инициализироваться в любом порядке, с пропущенными полями заполняемые нулями.
Таким образом, можно объявить:

```golang
return &File{fd: fd, name: name}
```

В предельном случае, когда составной литерал без полей вообще, то создание нулевым значением будет тип. Выражения `new(File)` и `&File{}` одинаковы.

Составные литералы могут также создавать массивы, срезы, карты, с пометкой полей как индексов или ключами карт.
К примеру, инициализированные значения `Enone`, `Eio`, и `Einval` разные.

```golang
a := [...]string {Enone: "no error", Eio: "Eio", Einval: "invalid argument"}
s := []string {Enone: "no error", Eio: "Eio", Einval: "invalid argument"}
m := map[int]string{Enone: "no error", Eio: "Eio", Einval: "invalid argument"}
```

[^](#Оглавление)

### Создание с помощью `make`

[^](#Оглавление)

Возвращаясь к созданию элементов.
Встроенная функция `make(T, `*args*`)` служит для других целей нежели `new(T)`.
Он создает только срезы, карты и каналы, и возвращают *инициализированные* (не нулевые) значение типа `T` (а не `*T`).
Причиной различия для этих трех типов, в том что внутри они представляют из себя структуры данных, которые необходимо инициализировать перед использованием.
К примеру, срезы - это трехэлементная структура, содержащая указатель на данные(внутри массив), длину, и емкость, причём пока все элементы не инициализированы - срез *нулевой* `nil`.
Для срезов, карт и каналов, встроенная команда `make` инициализирует внутреннюю структуру данных и подготавливает значения к использованию.

К примеру:

```golang
make([]int, 10, 100)
```

создает массив из 100 значений типа `int` и затем создает структуру среза длинной 10 и емкостью 100 со ссылкой только на первые 10 элементов.
(Когда создается слайс, его емкость задавать не обязательно, смотрите раздел посвящённый срезам.)
В противоположность, `new([]int)` возвращает указатель на новый, созданный, заполненный нулями срез, это указатель на значение `nil` среза.

Эти примеры показывают различие между `new` и `make`.

```golang
var p *[]int = new([]int) // allocates slice structure; *p == nil; rarely useful
var v []int = make([]int, 100) // the slice v now refers to a new array of 100 ints

// Unnecessarily complex:
var p *[]int = new([]int)
*p = make([]int, 100, 100)

// Idiomatic:
v := make([]int, 100)
```

Помните что `make` используется только для карт, срезов и каналов и не возвращают указатель.
Для получения указателя в явном виде используйте `new` или возьмите указатель в явном виде.

[^](#Оглавление)

### Массивы

[^](#Оглавление)

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

Какие основные отличия между обращением с массивами между языками Go и C:
* Массивы значений. Присвоение одно массива другому копирует все элементы.
* Если вы передаёте массив в функцию, то передаётся копия массива, а не указатель на него.
* Размер массива является частью массива. Типы `[10]int` и `[20]int` разные.

Массивы могут быть полезными, но дорогими(с точки зрения производительности) и если Вы хотите иметь гибкость и эффективность схожее с поведением в языке C-like, то необходимо использовать в функциях указатели.

```golang
func Sum(a *[3]float64) (sum float64) {
for _, v := range *a {
sum += v
}
return
}

array := [...]float64{7.0, 8.5, 9.1}
x := Sum(&array) // Note the explicit address-of operator
```

Но данный стиль не подходит Go.
Используйте срезы вместо массивов.

[^](#Оглавление)

### Срезы(Slices, слайсы)

[^](#Оглавление)

Срезы это обёртка для массивов и при этом более общий и мощный, и предоставляет собой более удобный интерфейс по управлению данными, в случаях, когда не известно точное количество элементов и необходимо преобразование размера массивов.
Большинство программ на языке Go, выполнены с использованием срезов, а не простых массивов.

Срез хранит ссылку на массив и поэтому если приравнять срез к другому срезу, то будет тот же массив.
Если срез является аргументом функции, то изменения элементов в срезе будут видны вызывающему данному функцию, это аналогично передаче указателя на базовый массив.
В функция `Read` может принимать в качестве аргумента срез, что равнозначно указателю на массив и длины массива; длина среза указывает верхний предел количество данных которые необходимо прочитать.
В данном случае тип `File` пакета `os` имеет следующую сигнатуру метода `Read`:

```golang
func (f * File) Read(buf []byte) (n int, err error)
```

Метод возвращает количество прочитанных байт или если есть, то ошибку.
Для чтения первых 32 байт в буфере `buf`, *получить(срезать) часть* буфера.

```golang
n, err := f.Read(buf[0:32])
```

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

```golang
var n int
var err error
for i := 0; i < 32; i++ {
nbytes, e := f.Read(buf[i:i+1]) // Read one byte.
if nbytes == 0 || e != nil {
err = e
break
}
n += nbytes
}
```

Длина среза может меняться, пока не исчерпает размер внутреннего массива.
С помощью встроенной функции `cap` можно узнать *емкость* среза, представляющий максимальную длину среза.
В следующем примере рассматривается функция для добавления данных в срез.
Если данные превышают ёмкость среза, то срез необходимо переопределить.
Функция `Append` возвращает результирующий срез. Функция использует тот факт что использование `len` и `cap` допустимо, даже если у нас имеется нулевой срез `nil` - при этом возвращая 0.

```golang
func Append(slice, data []byte) []byte {
l := len(slice)
if l + len(data) > cap(slice) { // reallocate
// Allocate double what's needed, for future growth.
newSlice := make([]byte, (l+len(data))* 2)
// The copy function is predeclared and works for any slice type.
copy(newSlice, slice)
slice = newSlice
}
slice = slice[0:l+len(data)]
for i, c := range data {
slice[l+i] = c
}
return slice
}
```

**TODO**
We must return the slice afterwards because, although `Append`
can modify the elements of `slice`, the slice itself (the run-time data
structure holding the pointer, length, and capacity) is passed by value.
**-**

Добавление элементов в срез настолько популярно, что функция `append` стала встроенной. Для того чтобы понять принцип работы данной функции нам необходимо больше информации, поэтому мы вернёмся к этому позже.

[^](#Оглавление)

### Двухмерные срезы

[^](#Оглавление)

Массивы и срезы в Go - одномерные.
Для создания двухмерного массива или среза, нам необходимо определять массив-массивов или срез-срезов, как в примере:

```golang
type Transform [3][3]float64 // A 3x3 array, really an array of arrays.
type LinesOfText [][]byte // A slice of byte slices.
```

В связи с тем, что срезы переменной длины, то допустимо иметь каждый внутренний срез разной длины.
Это наиболее общая ситуация, как в примере `LinesOfText`, в котором каждая строка имеет независимую длину.

```golang
text := LinesOfText{
[]byte("Now is the time"),
[]byte("for all good gophers"),
[]byte("to bring some fun to the party."),
}
```

Иногда необходимо создавать двухмерные срезы, к примеру при обработки пикселей.
Есть 2 способа для этого:
* Первый, создание каждого среза независимо
* Второй, создание простого массива срезов.
Наилучший способ выбирается в зависимости от программы.
Если срез можно увеличивать или уменьшать, они должны быть независимы, для того чтобы избежать перезаписи новых строк. Если не требуется изменять размер, то наиболее эффективным был бы способ с создание одним их аллоцированием(инициализацией).
Рассмотрим оба способа.

```golang
// Allocate the top-level slice.
picture := make([][]uint8, YSize) // One row per unit of y.
// Loop over the rows, allocating the slice for each row.
for i := range picture {
picture[i] = make([]uint8, XSize)
}
```

с одним созданием:

```golang
// Allocate the top-level slice, the same as before.
picture := make([][]uint8, YSize) // One row per unit of y.
// Allocate one large slice to hold all the pixels.
pixels := make([]uint8, XSize*YSize) // Has type []uint8 even though picture is [][]uint8.
// Loop over the rows, slicing each row from the front of the remaining pixels slice.
for i := range picture {
picture[i], pixels = pixels[:XSize], pixels[XSize:]
}
```

[^](#Оглавление)

### Карты(Maps)

[^](#Оглавление)

Карты - это удобная и мощная встроенная структура данных, связывающая значение одного типа(*ключ (key)*) со значением другого типа (*элемент (element)* или *значение (value)*).
Ключ может быть любого типа, для которого определён оператор равно, как для целых чисел, чисел с плавающей точкой или комплексные числа, строки, указатели, интерфейсы (если динамические типы поддерживают равенство), структуры и массивы.
Срезы не используются в качестве ключа для карт, так как равенство не определено для них.
Карты, также как и срезы, имеют внутреннюю структуру данных.
Если Вы передадите карту в функции и измените содержание карты, то изменения останутся для вызывающего.
Карты могут быть созданы с использованием синтаксиса составных литералов с разделением по колонкам пар ключ-значение, поэтому легко создать начальные данные.

```golang
var timeZone = map[string]int{
"UTC": 0*60*60,
"EST": -5*60*60,
"CST": -6*60*60,
"MST": -7*60*60,
"PST": -8*60*60,
}
```

Добавление и получение значений из карт, синтаксически, выглядит как для массивов или срезов, за тем исключением того что индекс не обязательно должен быть целым числом.

```golang
offset := timeZone["EST"]
```

При попытке получения значения из карты по ключу, которого нет в карте, приведёт к возвращению нулевого значения.
К примеру, если карта содержит целые числа, как описывалось выше, для несуществующего ключа будет возвращено `0`.
Это можно представить как карту у которой в качестве типа значения используется `bool`. Добавление записи в карту это как добавление со значением `true` в карту и дальнейшая простая проверка на индексирование.

```golang
attended := map[string]bool{
"Ann": true,
"Joe": true,
...
}

if attended[person] { // will be false if person is not in the map
fmt.Println(person, "was at the meeting")
}
```

Иногда необходимо отличать отсутствие записи от нулевого значения. К примеру, есть ли запись для `"UTC"` или это пустая строка потому что отсутствует значение в карте?
Для того чтобы отличить - Вы можете использовать множественное присвоение.

```golang
var seconds int
var ok bool
seconds, ok = timeZone[tz]
```

Очевидная причина называть данную идиому "запятая ок".
В данном примере, если `tz` существует, то `seconds` будет иметь необходимое значение и `ok` будет `true`, но если не существует, то `seconds` будет иметь нулевое значение а `ok` будет `false`.
В следующем примере, представлена функция с хорошим описанием ошибки:

```golang
func offset(tz string) int {
if seconds, ok := timeZone[tz]; ok {
return seconds
}
log.Println("unknown time zone:", tz)
return 0
}
```

В случаи, если нас не интересует само значение, а лишь его наличие, то можно использовать **пустой идентификатор `_`**, расположенный вместо значения.

```golang
_ , present := timeZone[tz]
```

Для удаления записи из карты, необходимо использовать встроенную функцию `delete`, где в качестве аргументов задаётся карта и ключ для удаления.
Данная операция безопасна, даже если данного ключа уже нет в карте.

```golang
delete(timeZone, "PDT") // Now on Standard Time
```

[^](#Оглавление)

### Печать(Printing)

[^](#Оглавление)

Форматированная печать в Go подобна стилю в языке C `printf`, но более богаче и более обобщенное. Необходимые функции расположены в пакете `fmt` и имеют названия с большой буквы: `fmt.Printf`, `fmt.Fprintf`, `fmt.Sprintf` и так далее. Функции (`Sprintf` и другие) возвращают строку, а не заполняют предоставленный буфер.

Вам нет необходимости в создании форматировании строк, так как для каждой `Printf`, `Fprintf` and `Sprintf` есть пара функций к примеру `Print` и `Println`.

Данные функции не берут формат строки, а вместо этого устанавливают форматирование по умолчанию для каждого аргумента. Функция `Println` также добавляет пробел между аргументами и добавляет разрыв строки в конце строки. Функция `Print` добавляет пробел только той же строке.
В примере каждая строка производит одинаковый результат.

```golang
fmt.Printf("Hello %d\n", 23)
fmt.Fprint(os.Stdout, "Hello ", 23, "\n")
fmt.Println("Hello", 23)
fmt.Println(fmt.Sprint("Hello ", 23))
```

Для форматированной печати функцией `fmt.Fprint` и его друзьями, принимают в качестве первого аргумента объект реализующий интерфейс `io.Writer`.
Значения `os.Stdout` и `os.Stderr` знакомы.

Следующее расходится с реализацией на языке С. Первое, числовые форматы `%d` не имеют флагов знаковости или размера; Вместо этого, функции печати используют тип аргумента для задания свойств.

```golang
var x uint64 = 1<<64 - 1
fmt.Printf("%d %x; %d %x\n", x, x, int64(x), int64(x))
```

печатает

```
18446744073709551615 ffffffffffffffff; -1 -1
```

Если вы используете соглашение по умолчанию, то для целых чисел можно использовать обобщенный формат `%v` (для "значений"); и результат будет одинаков как для `Print` так и для `Println`.

Более того, данный формат может напечатать *любое* значение, даже срез, структуру или карту.
Печать карты временной зоны из предыдущего раздела.

```golang
fmt.Printf("%v\n", timeZone) // or just fmt.Println(timeZone)
```

который печатает следующий результат

```
map[CST:-21600 PST:-28800 EST:-18000 UTC:0 MST:-25200]
```

Ключи карт могут быть напечатаны в любом порядке.
При печати структуры, с аннотацией `%+v` производиться печать полей структуры с их именами и для каждого значения с форматом `%#v` печатается значение с полным синтаксисом Go.

```golang
type T struct {
a int
b float64
c string
}
t := &T{ 7, -2.35, "abc\tdef" }
fmt.Printf("%v\n", t)
fmt.Printf("%+v\n", t)
fmt.Printf("%#v\n", t)
fmt.Printf("%#v\n", timeZone)
```

печатает

```
&{7 -2.35 abc def}
&{a:7 b:-2.35 c:abc def}
&main.T{a:7, b:-2.35, c:"abc\tdef"}
map[string] int{"CST":-21600, "PST":-28800, "EST":-18000, "UTC":0, "MST":-25200}
```

(На заметку: обратите внимание на амперсанды)

Для ссылок на строки подходит `%q`, который принимает значение на `string` или `[]byte`.
Альтернативный формат `%#q` будет использовать обратные кавычки, если это возможно.
(Формат `%q` также допустим для целых чисел и рун, создавая односсылочные константы рун.)
Также, `%x` работает со строками, массивом байт и срезом байт также как с целыми числами, создаёт шестнадцатеричные целые строки, а с пробелом в формате (`% x`) добавляет пробелы между байтами.

Другой удобный формат `%T`, который печатает *тип* значения.

```golang
fmt.Printf("%T\n", timeZone)
```

печатает

```
map[string] int
```

Если Вы хотите свой собственный формат типа, то для этого достаточно метод с сигнатурой `String() string` для Вашего типа.
Для нашего простого примера, тип `T`, выглядит следующим образом.

```golang
func (t * T) String() string {
return fmt.Sprintf("%d/%g/%q", t.a, t.b, t.c)
}
fmt.Printf("%v\n", t)
```

Печатает в следующем формате

```
7/-2.35/"abc\tdef"
```

(Если Вам необходимо напечатать *значение* типа `T` как указателя на тип `T`, то метод `String` должен иметь значение типа; этот пример использует указатель, т.к. они более эффективны и идиоматичны типу структуры.)

Наша функция `String` может вызывать `Sprintf`, потому что функция печати возвращаемая и поэтому можно её обернуть. Это важно для понимания данного подхода.
Однако, не создавайте функцию `String` вызывающую метод `Sprintf`, в случаи если далее будет рекурсивно вызвана `String`.
Это может произойти если `Sprintf` вызывает на печать строку получателя, который вызовет функцию снова. Эту ошибку можно легко создать и она показана на следующем примере.

```golang
type MyString string

func (m MyString) String() string {
return fmt.Sprintf("MyString=%s", m) // Error: will recur forever.
}
```

Для того чтобы решить эту проблему, необходимо изменить аргумент на базовый тип, который не имеет функции.

```golang
type MyString string
func (m MyString) String() string {
return fmt.Sprintf("MyString=%s", string(m)) // OK: note conversion.
}
```

Другой способ печати это допустить печать функции аргументов напрямую в другую функцию.
Сигнатура `Printf` используется для типов `...interface{}`, что допускает произвольное число аргументов, которые добавляются после формата *format*.

```golang
func Printf(format string, v ...interface{}) (n int, err error) {
```

**TODO**
Within 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.
Here is the implementation of the function `log.Println` we used above. It passes its arguments directly to `fmt.Sprintln` for the actual formatting.
**-**

```golang
// Println prints to the standard logger in the manner of fmt.Println.
func Println(v ...interface{}) {
std.Output(2, fmt.Sprintln(v...)) // Output takes parameters (int, string)
}
```

Запись `...` после `v` при вызове функции `Sprintln` объявляет компилятору о том что `v` является списком аргументов; с другой стороны `v` воспринимается как простой срез аргументов.

Если Вам необходимо большее количество информации, то смотрите документацию `godoc` в пакете `fmt`.

Кстати параметр `...` может иметь тип, для примера`...int` для функции определения минимума используется список целых чисел:

```golang
func Min(a ...int) int {
min := int(^uint(0) >> 1) // largest int
for _ , i := range a {
if i < min {
min = i
}
}
return min
}
```

[^](#Оглавление)

### Присоединение(Append)

[^](#Оглавление)

В настоящий момент? пришел момент для разъяснения конструкции встроенной функции `append`. Сигнатура функции `append` отличается от ранее описанной функции `Append`.
Схематично, выглядит следующим образом:

```golang
func append(slice []*T*, elements ...*T*) []*T*
```

где *T* любой тип. Вы не можете написать в языке Go функцию в которой `T` определена вызывающим. Поэтому необходима поддержка компилятора для функции `append`.

Данная функция `append` добавляет элемент в конец среза и возвращает результат.
Причина возврата результата, в том что как и в рукописной функции `Append` массив может измениться.
Простой пример:

```golang
x := []int{1,2,3}
x = append(x, 4, 5, 6)
fmt.Println(x)
```

печатает `[1 2 3 4 5 6]`. Итак, `append` работает в принципе как `Printf` с произвольным количеством аргументов.

Но что если необходимо добавить срез в срез, как в нашей реализации `Append`? Все просто: используем `...` который мы использовали в `Output`. Вот пример кода для получение того же результата.

```golang
x := []int{1,2,3}
y := []int{4,5,6}
x = append(x, y...)
fmt.Println(x)
```

Обращаю внимание, что без `...` компилятор напишет ошибку, так как `y` не имеет тип `int`.

[^](#Оглавление)

## Инициализация(Initialization)

[^](#Оглавление)

Инициализация в языке Go более мощный инструмент нежели в языках С или С++.
Даже сложные структуры можно инициализировать. Упорядочивание между инициализируемыми объектами разных пакетов, обрабатывается корректно.

[^](#Оглавление)

### Константы(Constants)

[^](#Оглавление)

Константы в Go это просто константы.
Они создаются во время компиляции даже если она определена в локальной функции и могут быть цифры, символы(руны), строки или булевый тип.
Из-за ограничения времени компиляции, компилятор должен определять какие выражения могут быть константами. К примеру, выражение `1<<3` это константное выражение, в то время как выражение `math.Sin(math.Pi/4)` не является константой, так как вызывает функцию `math.Sin` требующую выполнения по время выполнения.

В языке Go, перечисление констант производиться с помощью перечислителя **`iota`**. Так как `iota` может быть неявно повторяемой для выражения или выражений, то легко можно строить сложные наборы значений.

```golang
//{{code "/doc/progs/eff_bytesize.go" `/^type ByteSize/` `/^\)/`}}
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import "fmt"

type ByteSize float64

const (
_ = iota // ignore first value by assigning to blank identifier
KB ByteSize = 1 << (10 * iota)
MB
GB
TB
PB
EB
ZB
YB
)
```

Использование функции `String` к пользовательским типам производить печать необходимым образом.
**TODO**
Although you'll see it most often applied to structs, this technique is also useful for scalar types such as floating-point types like `ByteSize`.
**-**

```golang
//See code "/doc/progs/eff_bytesize.go"

func (b ByteSize) String() string {
switch {
case b >= YB:
return fmt.Sprintf("%.2fYB", b/YB)
case b >= ZB:
return fmt.Sprintf("%.2fZB", b/ZB)
case b >= EB:
return fmt.Sprintf("%.2fEB", b/EB)
case b >= PB:
return fmt.Sprintf("%.2fPB", b/PB)
case b >= TB:
return fmt.Sprintf("%.2fTB", b/TB)
case b >= GB:
return fmt.Sprintf("%.2fGB", b/GB)
case b >= MB:
return fmt.Sprintf("%.2fMB", b/MB)
case b >= KB:
return fmt.Sprintf("%.2fKB", b/KB)
}
return fmt.Sprintf("%.2fB", b)
}
```

Выражение `YB` печатается как `1.00YB`, когда `ByteSize(1e13)` печатает как `9.09TB`.

Используемый здесь `Sprintf` в функции `String` типа `ByteSize` безопасна(не вызывается рекурсивно), не потому что происходит конвертирование, а потому что вызывается функция `Sprintf` с `%f`, который не строковый формат:`Sprintf` будет вызывать функцию `String`, функцию которой необходима строка и `%f` число с плавающей точкой.

[^](#Оглавление)

### Переменные(Variables)

[^](#Оглавление)

Переменные могут инициализироваться как константы, но инициализация производиться во время работы.

```golang
var (
home = os.Getenv("HOME")
user = os.Getenv("USER")
gopath = os.Getenv("GOPATH")
)
```

[^](#Оглавление)

### Функция init

[^](#Оглавление)

Каждый исходный код может определить свою первичную функцию `init` для обязательных настройки. (На самом деле файл может иметь несколько функций `init`.)
Функция `init` вызывается после всех объявлений переменных и после всех объявлений переменных всех пакетов.

Общее применение функции `init` в проверки или починки состояния программы до начала реального исполнения.

```golang
func init() {
if user == "" {
log.Fatal("$USER not set")
}
if home == "" {
home = "/home/" + user
}
if gopath == "" {
gopath = home + "/go"
}
// gopath may be overridden by --gopath flag on command line.
flag.StringVar(&gopath, "gopath", gopath, "override default GOPATH")
}
```

[^](#Оглавление)

## Методы(Methods)

[^](#Оглавление)

### Указатели или Значения

[^](#Оглавление)

Как мы видели в примеры с `ByteSize`, функции может иметь имя типа (кроме указателей или интерфейсов) и приемник не обязательно должен иметь структуры.

Как обсуждалось ранее в срезах, мы написали функцию `Append`.
Мы можем определить функции вместе со срезом. Для этого, мы объявим именованный тип, который мы можем связать с функцией и там самым создать получателя данной функции для значений этого типа.

```golang
type ByteSlice []byte

func (slice ByteSlice) Append(data []byte) []byte {
// Body exactly the same as the Append function defined above.
}
```

Данный метод все также возвращает обновленный срез. Для решения этой неуклюжести можно воспользоваться *указателем* на `ByteSize` в получатель, итак можно переписать следующим образом:

```golang
func (p *ByteSlice) Append(data []byte) {
slice := *p
// Body as above, without the return.
*p = slice
}
```

На самом деле, мы можем сделать это ещё лучше. Если мы изменим функцию, то она будет выглядеть как стандартная функция `Write`, то есть вот так,

```golang
func (p *ByteSlice) Write(data []byte) (n int, err error) {
slice := *p
// Again as above.
*p = slice
return len(data), nil
}
```

тип `*ByteSlice` удовлетворяет стандартному интерфейсу `io.Writer`, что удобно. Например, мы можем напечатать один из них:

```golang
var b ByteSlice
fmt.Fprintf(&b, "This hour has %d days\n", 7)
```

Мы передаем адрес `ByteSlice`, поскольку только `*ByteSlice` удовлетворяет интерфейсу `io.Writer`.
Правило получателя *о указателях или значениях*, в том что функции значения могут использоваться для указателей и значений, а функция указателя может только использовать указатель.

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

К примеру, переменная `b` адресованная, поэтому мы можем вызвать функцию `Write` просто вызвав `b.Write`.
Компилятор сам допишет `(&b).Write` за нас.

Кстати, идея использования `Write` на срезах байт наиважнейшая для реализации `bytes.Buffer`.

[^](#Оглавление)

## Интерфейсы и другие типы

[^](#Оглавление)

### Интерфейсы

[^](#Оглавление)

Интерфейсы в Go позволяют создать особое поведения для объектов: *Если нечто может делать* **это** *, то это можно использовать* **здесь**. Мы уже это встречали в простых примерах, когда реализовывали функцию `String` для печати, в то время как `Fprintf` может выдавать на печать другое с методом `Write`.
Интерфейсы с одним или двумя функциями свойственны в языке Go, как `io.Writer` реализующий `Write`.

Любой тип может реализовывать множество интерфейсов.
К примеру, коллекции могут быть отсортированы с помощью функций из пакета `sort`, если она реализует `sort.Interface`, который состоит из `Len()`, `Less(i, j int) bool`, и `Swap(i, j int)` и это может задать собственный формат.
Рассмотрим пример `Sequence`

```golang
//{{code "/doc/progs/eff_sequence.go" `/^type/` "$"}}
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
"fmt"
"sort"
)

func main() {
seq := Sequence{6, 2, -1, 44, 16}
sort.Sort(seq)
fmt.Println(seq)
}

type Sequence []int

// Methods required by sort.Interface.
func (s Sequence) Len() int {
return len(s)
}
func (s Sequence) Less(i, j int) bool {
return s[i] < s[j]
}
func (s Sequence) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}

// Method for printing - sorts the elements before printing.
func (s Sequence) String() string {
sort.Sort(s)
str := "["
for i, elem := range s {
if i > 0 {
str += " "
}
str += fmt.Sprint(elem)
}
return str + "]"
}
```

[^](#Оглавление)

### Преобразование (Conversions)

[^](#Оглавление)

Функция `String` работает с `Sequence` и `Sprint` уже работает со срезами. Мы может распространить данный эффект, если конвертируем `Sequence` на `[]int` до вызова `Sprint`.

```golang
func (s Sequence) String() string {
sort.Sort(s)
return fmt.Sprint([]int(s))
}
```

Это функция другой пример техники конвертирования для вызова `Sprintf` безопасно для функции `String`.
Так как два типа (`Sequence` и `[]int`) одинаковы, то мы можем игнорировать имя типа, это допустимое конвертирование между ними.
При конвертации не происходит создание нового значения, это временная замена существующего значения на новый тип.
(При других допустимых конвертациях, к примеру из целого числа в число с плавающей точкой, происходит создание нового значения.)

Это идиоматично в программе Go - конвертация типа позволяет получить доступ к другим функциям. К примеру, мы можем использовать существующий тип `sort.IntSlice`:

```golang
type Sequence []int

// Method for printing - sorts the elements before printing
func (s Sequence) String() string {
sort.IntSlice(s).Sort()
return fmt.Sprint([]int(s))
}
```

Теперь, наш `Sequence` реализует множество интерфейсов (сортировка и печать), мы можем использовать множество типов (`Sequence`, `sort.IntSlice` и `[]int`), которые выполняют определенную часть работ.
Это не типично в использовании, но эффективно.

[^](#Оглавление)

### Конвертация интерфейсов и привязка типов

[^](#Оглавление)

Переключатель типов(Type switches) является одной из форм конвертации: на основе интерфейса и переключателя для каждого элемента, в некотором смысле преобразует тип в элемент переключателя.
Это простой вариант как в коде `fmt.Printf` конвертирует значение в строку, используя переключатель типа.
И если это уже строка, мы хотим чтобы фактическое значение происходило по его интерфейсу, но в случаи если она имеет функцию `String`, то хотим чтобы в результате вызывалась именно она.

```golang
type Stringer interface {
String() string
}

var value interface{} // Value provided by caller.
switch str := value.(type) {
case string:
return str
case Stringer:
return str.String()
}
```

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

Что если, мы будем беспокоиться лишь об одном типе? Если мы