Примечания к выпуску Go 1.1

Введение в Go 1.1

Выпуск языка программирования Go версии 1 (Go 1 или Go 1.0, как его часто называют) в марте 2012 года внедрил новую эпоху стабильности в языке программирования Go и его библиотеках. Эта стабильность помогла развиваться растущему сообществу пользователей Go и систем по всему миру. С тех пор было выпущено несколько «точечных» релизов — 1.0.1, 1.0.2 и 1.0.3. Эти релизы исправляли известные ошибки, но не вносили никаких не критичных изменений в реализацию.

Новый выпуск, Go 1.1, сохраняет обещание совместимости, но добавляет несколько значительных (обратно совместимых, конечно) изменений в язык, имеет длинный список (опять же совместимых) изменений в библиотеках, а также включает важную работу по реализации компиляторов, библиотек и среды выполнения. Акцент сделан на производительности. Бенчмарки — это в лучшем случае неточная наука, но мы видим значительные, иногда радикальные ускорения для многих наших тестовых программ. Мы уверены, что многие программы наших пользователей также получат улучшения просто обновив установку Go и перекомпилировав их.

Этот документ кратко описывает изменения между Go 1 и Go 1.1. Очень мало (если вообще что-либо) кода потребует модификации для запуска с Go 1.1, хотя несколько редких случаев ошибок могут проявиться с этим релизом и потребуют корректировки, если они возникнут. Подробности представлены ниже; обратите внимание на обсуждение 64-битных целых чисел и Unicode литералов.

Изменения в языке

Документ о совместимости Go гарантирует, что программы, написанные согласно спецификации языка Go 1, продолжат работать, и эти обещания остаются в силе. В целях уточнения спецификации, однако, были уточнены некоторые детали о случаях ошибок. Также были добавлены некоторые новые языковые возможности.

Целочисленное деление на ноль

В Go 1 целочисленное деление на константный ноль вызывало панику во время выполнения:

<code>func f(x int) int {
  return x/0
}
</code>

В Go 1.1 целочисленное деление на константный ноль является недопустимой программой, поэтому это ошибка компиляции.

Суррогатные пары в Unicode литералах

Определение строковых и рунных литералов было уточнено для исключения половин суррогатных пар из множества допустимых кодовых точек Unicode. См. раздел Unicode для получения дополнительной информации.

Значения методов

Go 1.1 теперь реализует значения методов, которые представляют собой функции, связанные с конкретным значением получателя. Например, заданное значение Writer w, выражение w.Write, значение метода, является функцией, которая всегда будет записывать в w; оно эквивалентно литералу функции, замыкающей w:

<code>func (p []byte) (n int, err error) {
  return w.Write(p)
}
</code>

Значения методов отличаются от выражений методов, которые генерируют функции из методов заданного типа; выражение метода (*bufio.Writer).Write эквивалентно функции с дополнительным первым аргументом, получателем типа (*bufio.Writer):

<code>func (w *bufio.Writer, p []byte) (n int, err error) {
  return w.Write(p)
}
</code>

Обновление: Никакой существующий код не затронут; изменение полностью обратно совместимо.

Требования к возврату

До Go 1.1 функция, возвращающая значение, требовала явного «return» или вызова panic в конце функции; это было простой способ заставить программиста явно выражать смысл функции. Но есть много случаев, где финальный «return» явно не нужен, например, функция с бесконечным циклом «for».

В Go 1.1 правило, касающееся финальных инструкций return, стало более гибким. Вводится концепция завершающей инструкции, инструкции, которая гарантированно является последней, выполняемой функцией. Примерами могут служить циклы for без условия и инструкции if-else, в которых каждая часть заканчивается инструкцией return. Если финальная инструкция функции может быть показана синтаксически как завершающая инструкция, финальная инструкция return не требуется.

Обратите внимание, что правило является исключительно синтаксическим: оно не обращает внимания на значения в коде и, следовательно, не требует сложного анализа.

Обновление: Изменение обратно совместимо, но существующий код с избыточными инструкциями return и вызовами panic может быть упрощён вручную. Такой код может быть выявлен с помощью go vet.

Изменения в реализациях и инструментах

Состояние gccgo

График выпусков GCC не совпадает с графиком выпусков Go, поэтому некоторое расхождение в выпусках gccgo неизбежно. Версия GCC 4.8.0, выпущенная в марте 2013 года, включает почти полную версию gccgo для Go 1.1. Её библиотека немного отстаёт от выпуска, но основное отличие заключается в том, что значения методов не реализованы. Время от времени примерно в июле 2013 года ожидается выпуск GCC 4.8.2 с gccgo, обеспечивающим полную реализацию Go 1.1.

Разбор флагов командной строки

В инструментарии gc компиляторы и компоновщики теперь используют одни и те же правила разбора флагов командной строки, что и пакет flag в Go, что отличается от традиционного Unix-стиля разбора флагов. Это может повлиять на скрипты, которые напрямую вызывают инструмент. Например, go tool 6c -Fw -Dfoo теперь должно быть записано как go tool 6c -F -w -D foo.

Размер int на 64-битных платформах

Язык позволяет реализации выбирать, являются ли типы int и uint 32-битными или 64-битными. Предыдущие реализации Go делали int и uint 32-битными на всех системах. Обе реализации — gc и gccgo — теперь делают int и uint 64-битными на 64-битных платформах, таких как AMD64/x86-64. Среди прочего, это позволяет выделять срезы с более чем 2 миллиардами элементов на 64-битных платформах.

Обновление: Большинство программ не пострадают от этого изменения. Поскольку Go не позволяет неявных преобразований между различными числовыми типами, никакие программы не перестанут компилироваться из-за этого изменения. Однако программы, содержащие неявные предположения о том, что int имеет размер только 32 бита, могут изменить своё поведение. Например, следующий код выводит положительное число на 64-битных системах и отрицательное — на 32-битных:

<code>x := ^uint32(0) // x is 0xffffffff
i := int(x)     // i is -1 on 32-bit systems, 0xffffffff on 64-bit
fmt.Println(i)
</code>

Портативный код, предназначенный для 32-битного расширения знака (что даёт -1 на всех системах), вместо этого должен использовать:

<code>i := int(int32(x))
</code>

Размер кучи на 64-битных архитектурах

На 64-битных архитектурах максимальный размер кучи был значительно увеличен, с нескольких гигабайт до нескольких десятков гигабайт. (Точные детали зависят от системы и могут изменяться.)

На 32-битных архитектурах размер кучи не изменился.

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

Unicode

Чтобы сделать возможным представление кодовых точек больше 65535 в UTF-16, Unicode определяет полу-суррогаты, диапазон кодовых точек, используемых только при сборке больших значений и только в UTF-16. Кодовые точки из этого диапазона являются недопустимыми для любого другого использования. В Go 1.1 это ограничение соблюдается компилятором, библиотеками и средой выполнения: полу-суррогат недопустим как значение rune, когда закодирован в UTF-8, или когда закодирован изолированно в UTF-16. При обнаружении, например, при преобразовании из rune в UTF-8, оно рассматривается как ошибка кодирования и даёт заменяющий символ, utf8.RuneError, U+FFFD.

Эта программа,

<code>import "fmt"

func main() {
  fmt.Printf("%+q\n", string(0xD800))
}
</code>

выводила "\ud800" в Go 1.0, но выводит "\ufffd" в Go 1.1.

Полузначения суррогатных символов Unicode теперь запрещены в константах rune и string, поэтому константы вроде '\ud800' и "\ud800" теперь отвергаются компиляторами. Когда такие строки записаны явно в виде байтов в кодировке UTF-8, они всё ещё могут быть созданы, как в "\xed\xa0\x80". Однако, когда такая строка декодируется как последовательность rune, как в цикле range, она будет давать только значения utf8.RuneError.

Маркер порядка байтов Unicode U+FEFF, закодированный в UTF-8, теперь разрешён как первый символ исходного файла Go. Несмотря на то, что его появление в байтовой кодировке UTF-8 без порядка байтов очевидно избыточно, некоторые редакторы добавляют этот маркер как своего рода «магическое число», идентифицирующее файл в кодировке UTF-8.

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

Детектор гонок

Одним из крупных дополнений к инструментам является детектор гонок — способ находить ошибки в программах, вызванные одновременным доступом к одной и той же переменной, при котором хотя бы один из доступов является записью. Эта новая функциональность встроена в утилиту go. На данный момент она доступна только на Linux, Mac OS X и Windows системах с 64-битными процессорами x86. Чтобы включить её, установите флаг -race при сборке или тестировании вашей программы (например, go test -race). Детектор гонок документирован в отдельной статье.

Ассемблеры gc

Из-за изменения типа int до 64 бит и новой внутренней представления функций, расположение аргументов функций на стеке изменилось в инструментарии gc. Функции, написанные на ассемблере, потребуют переработки, по крайней мере, для корректировки смещений указателей кадра.

Обновление: Команда go vet теперь проверяет, соответствуют ли функции, реализованные на ассемблере, прототипам функций Go, которые они реализуют.

Изменения в команде go

Команда go получила несколько изменений, направленных на улучшение опыта работы с Go для новых пользователей.

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

<code>$ go build foo/quxx
can't load package: package foo/quxx: cannot find package "foo/quxx" in any of:
/home/you/go/src/pkg/foo/quxx (from $GOROOT)
/home/you/src/foo/quxx (from $GOPATH)
</code>

Во-вторых, команда go get больше не разрешает использовать $GOROOT в качестве целевого расположения по умолчанию при загрузке исходного кода пакета. Чтобы использовать команду go get, теперь требуется действительный $GOPATH.

<code>$ GOPATH= go get code.google.com/p/foo/quxx
package code.google.com/p/foo/quxx: cannot download, $GOPATH not set. For more details see: go help gopath
</code>

Наконец, в результате предыдущего изменения команда go get также завершится ошибкой, если $GOPATH и $GOROOT установлены на одно и то же значение.

<code>$ GOPATH=$GOROOT go get code.google.com/p/foo/quxx
warning: GOPATH set to GOROOT (/home/you/go) has no effect
package code.google.com/p/foo/quxx: cannot download, $GOPATH must not be set to $GOROOT. For more details see: go help gopath
</code>

Изменения в команде go test

Команда go test больше не удаляет исполняемый файл при запуске с включённым профилированием, чтобы облегчить анализ профиля. Реализация автоматически устанавливает флаг -c, поэтому после выполнения,

<code>$ go test -cpuprofile cpuprof.out mypackage
</code>

файл mypackage.test будет оставлен в директории, из которой была выполнена команда go test.

Команда go test теперь может генерировать информацию профилирования, которая сообщает, где находятся заблокированные горутины, то есть, где они обычно зависают, ожидая события, такое как передача данных через канал. Информация предоставляется в виде профиля блокировок (а не в виде профиля использования CPU), включаемого с помощью опции -blockprofile команды go test. Выполните команду go help test для получения дополнительной информации.

Изменения в команде go fix

Команда fix, обычно запускаемая как go fix, больше не применяет исправления для обновления кода, написанного до Go 1, чтобы использовать API Go 1. Чтобы обновить код, написанный до Go 1, до Go 1.1, используйте инструментарий Go 1.0, чтобы сначала преобразовать код в формат Go 1.0.

Ограничения сборки

Тег “go1.1” был добавлен в список стандартных ограничений сборки. Это позволяет пакетам использовать новые возможности Go 1.1, оставаясь совместимыми с более ранними версиями Go.

Чтобы собрать файл только с Go 1.1 и выше, добавьте следующее ограничение сборки:

<code>// +build go1.1
</code>

Чтобы собрать файл только с Go 1.0.x, используйте обратное ограничение:

<code>// +build !go1.1
</code>

Дополнительные платформы

Инструментарий Go 1.1 добавляет экспериментальную поддержку платформ freebsd/arm, netbsd/386, netbsd/amd64, netbsd/arm, openbsd/386 и openbsd/amd64.

Для работы на freebsd/arm или netbsd/arm требуется процессор ARMv6 или новее.

Go 1.1 добавляет экспериментальную поддержку cgo на linux/arm.

Кросс-компиляция

При кросс-компиляции инструмент go по умолчанию отключает поддержку cgo.

Чтобы явно включить cgo, установите CGO_ENABLED=1.

Производительность

Производительность кода, скомпилированного с использованием набора инструментов gc Go 1.1, должна быть значительно лучше для большинства программ на Go. Типичные улучшения по сравнению с Go 1.0 составляют около 30%-40%, иногда значительно больше, но иногда меньше или даже не заметны. Слишком много мелких оптимизаций было внесено в инструменты и библиотеки, чтобы перечислить их все здесь, но следующие основные изменения заслуживают внимания:

  • Компиляторы gc генерируют лучший код во многих случаях, особенно для операций с плавающей точкой на 32-битной архитектуре Intel.
  • Компиляторы gc делают больше встраивания, включая некоторые операции в среде выполнения, такие как append и преобразования интерфейсов.
  • Добавлена новая реализация карт Go с существенным сокращением затрат памяти и времени процессора.
  • Сборщик мусора стал более параллельным, что может сократить задержки для программ, работающих на нескольких процессорах.
  • Сборщик мусора также стал более точным, что требует небольшого количества времени процессора, но может значительно уменьшить размер кучи, особенно на 32-битных архитектурах.
  • Благодаря более тесной интеграции среды выполнения и сетевых библиотек, требуется меньше переключений контекста при сетевых операциях.

Изменения в стандартной библиотеке

bufio.Scanner

Различные функции для сканирования текстового ввода в пакете bufio, ReadBytes, ReadString и особенно ReadLine, являются избыточно сложными для простых задач. В Go 1.1 был добавлен новый тип, Scanner, который упрощает выполнение простых задач, таких как чтение входных данных как последовательность строк или слов, разделённых пробелами. Он упрощает задачу, прерывая сканирование при проблемных входных данных, например, при чрезмерно длинных строках, и используя простой стандартный режим: построчное чтение с удалением окончания строки. Вот пример кода, который воспроизводит входные данные построчно:

<code>scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
  fmt.Println(scanner.Text()) // Println will add back the final '\n'
}
if err := scanner.Err(); err != nil {
  fmt.Fprintln(os.Stderr, "reading standard input:", err)
}
</code>

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

net

Разрешители протоколов в пакете net ранее были менее строги в отношении переданного имени сети. Хотя документация была ясной: все допустимые сети для ResolveTCPAddr — это "tcp", "tcp4" и "tcp6", реализация Go 1.0 тихо принимала любую строку. В реализации Go 1.1 возвращается ошибка, если сеть не является одной из этих строк. То же самое относится к другим разрешителям протоколов: ResolveIPAddr, ResolveUDPAddr и ResolveUnixAddr.

Предыдущая реализация ListenUnixgram возвращала UDPConn как представление конечной точки соединения. В реализации Go 1.1 вместо этого возвращается UnixConn, чтобы позволить чтение и запись с использованием методов ReadFrom и WriteTo.

Структуры данных IPAddr, TCPAddr и UDPAddr добавляют новое строковое поле с именем Zone. Код, использующий неименованные составные литералы (например, net.TCPAddr{ip, port}) вместо именованных литералов (net.TCPAddr{IP: ip, Port: port}), сломается из-за нового поля. Правила совместимости Go 1 позволяют такую замену: клиентский код должен использовать именованные литералы, чтобы избежать таких проблем.

Обновление: Чтобы исправить проблемы, вызванные новым полем структуры, go fix перепишет код, добавив теги для этих типов. В более общем случае, go vet определит составные литералы, которые следует переписать с использованием тегов полей.

reflect

В пакете reflect есть несколько значительных дополнений.

Теперь возможно выполнить инструкцию “select” с использованием пакета reflect; см. описание Select и SelectCase для получения подробной информации.

Новый метод Value.Convert (или Type.ConvertibleTo) предоставляет функциональность для выполнения операции преобразования или утверждения типа Go на Value (или проверки возможности такого преобразования).

Новая функция MakeFunc создаёт обёртку функции, чтобы облегчить вызов функции с существующими Values, выполняя стандартные преобразования Go между аргументами, например, чтобы передать фактический int в формальный interface{}.

Наконец, новые функции ChanOf, MapOf и SliceOf создают новые Types из существующих типов, например, чтобы создать тип []T, заданный только T.

### **Изменения в Go 1.2** Go 1.2 — это значительное обновление, внесшее множество улучшений в стандартную библиотеку, производительность и инструменты разработки. Ниже приведен обзор основных изменений: --- #### **1. Стандартная библиотека** ##### **net/http** - Добавлен метод `ParseTime`, который парсит строки времени, используя несколько распространённых форматов HTTP. - Добавлен метод `PostFormValue` в `Request`, который аналогичен `FormValue`, но игнорирует URL-параметры. - Интерфейс `CloseNotifier` позволяет серверным обработчикам обнаруживать отключение клиента. - Метод `Handler` в `ServeMux` позволяет получить обработчик по пути без его выполнения. - `Transport` теперь может отменять запросы с помощью `CancelRequest`. - `Transport` более агрессивно закрывает TCP-соединения, когда `Response.Body` закрывается до полного чтения. ##### **net/mail** - Добавлены функции `ParseAddress` и `ParseAddressList` для парсинга адресов электронной почты по RFC 5322. ##### **net/smtp** - Добавлен метод `Hello` в типе `Client`, который отправляет сообщение `HELO` или `EHLO`. ##### **regexp** - Добавлен метод `Longest` для поддержки "leftmost-longest" соответствия (как в Unix). - Добавлен метод `Split`, который разбивает строки по регулярному выражению. ##### **strings** - Добавлены методы `TrimPrefix` и `TrimSuffix`. - Тип `Reader` теперь реализует интерфейс `io.WriterTo`. ##### **testing** - Добавлена функция `AllocsPerRun` для автоматической генерации статистики аллокаций. - Метод `ReportAllocs` в `B` выводит статистику памяти. - Добавлен метод `AllocsPerOp` в `BenchmarkResult`. - Добавлена функция `Verbose` для проверки флага `-v`. - Добавлен метод `Skip` в `T` и `B` для упрощения пропуска тестов. ##### **text/template и html/template** - Поддержка круглых скобок в шаблонах для группировки элементов. - Интерфейс `Node` получил два новых метода для улучшения отчетов об ошибках (влияет только на внутренние пакеты). ##### **os** - Добавлен метод `IsRegular` в `os.FileMode`, чтобы легко проверять, является ли файл обычным файлом. ##### **os/signal** - Добавлен метод `Stop`, который останавливает доставку сигналов в канал. ##### **sort** - Добавлена функция `Reverse`, которая меняет порядок сортировки. ##### **runtime/debug** - Добавлены функции: - `FreeOSMemory` — освобождает память ОС после сборки мусора. - `ReadGCStats` — получает статистику сборщика мусора. - `SetGCPercent` — управление частотой запуска GC, включая отключение. ##### **unicode** - Обновлена реализация до Unicode 6.2.0. - В `unicode/utf8` добавлена функция `ValidRune`, проверяющая, является ли рунный код допустимым. ##### **syscall** - Изменена сигнатура `Fchflags` в BSD (включая Darwin) — теперь принимает `int` вместо `string`. - Обновлены константы и системные вызовы для каждой поддерживаемой ОС. ##### **regexp** - Поддержка "leftmost-longest" соответствия через метод `Longest`. - Метод `Split` разбивает строки по регулярному выражению. ##### **net/textproto** - Добавлены функции `TrimBytes` и `TrimString` для ASCII-обрезки строк. --- #### **2. Производительность и инструменты** - **Сборка мусора (GC)**: Улучшена эффективность сборки мусора. - **Тестирование**: Автоматическое отслеживание аллокаций в тестах и бенчмарках. - **Сборка**: Улучшения в инструментах сборки (`go build`, `go install`, и т.д.). --- #### **3. Другие изменения** - **Консистентность**: Улучшена работа с Unicode, добавлены проверки валидности рун. - **Интерфейсы**: Некоторые изменения в интерфейсах, которые не должны повлиять на существующий код. - **Совместимость**: Несмотря на некоторые нарушения правил Go 1, изменения были сделаны с учётом безопасности. --- ### **Вывод** Go 1.2 принесла значительные улучшения в стандартной библиотеке, особенно в `net/http`, `testing`, `regexp`, `text/template`, `unicode` и `syscall`. Также улучшена производительность и инструментарий разработки, что делает Go 1.2 более мощным и удобным инструментом для разработки.
GoRu.dev Golang на русском

На сайте представлена адаптированная под русский язык документация языка программирования Golang