Вышел язык программирования Go 1.21: что нового и хорошего появилось? Оцениваем изменения
- понедельник, 21 августа 2023 г. в 00:00:25
Мы в МТС очень много всего разрабатываем на Golang, поскольку считаем этот язык программирования весьма достойным для проектов разного масштаба. На нём относительно просто писать, т. е. увеличивается скорость разработки, производительность — высокая, плюс есть защита от ошибок. И вот на днях был представлен Go 1.21. Что улучшили, изменили и добавили?
Разработчики внесли небольшое изменение в нумерацию версий. Так, ранее нумерация была представлена в форме Go 1.x для обозначения как глобальной версии языка Go, так и семейства версий, а также первой версии этого семейства. Теперь всё немного иначе. Начиная с Go 1.21, первая версия теперь — Go 1.X.0. Вот здесь представлено подробное описание того, как это работает сейчас.
Кроме того, сейчас реализована поддержка оптимизаций на основе результатов профилирования кода. Речь идёт о PGO — Profile-guided optimization. Одна из положительных черт поддержки — учёт особенностей, которые определяются во время выполнения программы. Соответственно, учёт профиля выполнения при сборке даёт возможность увеличить производительность приложений от 2% до 7%.
Улучшен вывод типов в обобщённых функциях, т. е. дженериках, которые предназначены для работы сразу с несколькими типами.
Плюс ко всему добавлена экспериментальная поддержка новой семантики обработки переменных в циклах. Соответственно, можно избежать типовых ошибок вследствие специфического поведения при использовании замыканий с подпрограмм в итерациях. Также можно отметить то, что новая семантика предусматривает создание для каждой итерации цикла отдельного экземпляра переменной. Она объявляется в цикле “for” при помощи оператора “:=”.
Разработчики добавили в стандартную библиотеку новые пакеты, включая:
log/slog — функции для записи структурированных логов
slices — типовые операции со срезами (slice) любых типов. Например, предложены функции для сортировки, более быстрые и гибкие, чем функции из пакета sort
maps — полезные операции над отображениями (map) с любыми типами ключей и элементов
cmp — функции для сравнений упорядоченных значений
Компилятор пересобрали с добавлением PGO-оптимизаций. Это даёт возможность ускорить сборку программ на 2–4%. Не так много, но и немало.
Оптимизирован сборщик мусора, что дало возможность снизить задержки в ряде приложений вплоть до 40%.
Удалось снизить накладные расходы при трассировке кода при помощи пакета runtime/trace на системах с архитектурой amd64 и arm64.
Добавлены новые функции, включая min и max, для выбора наименьшего/наибольшего значения, а также функция clear для удаления или обнуления всех элементов в структурах map или slice.
Ну и последнее: в новой версии появился экспериментальный порт (GOOS=wasip1, GOARCH=wasm) для компиляции в промежуточный код WebAssembly, использующий API WASI (WebAssembly System Interface) для обеспечения обособленного запуска.
Как и говорилось выше, их три — это min и max, а также clear.
min и max
Они позволяют выбирать наименьшее и наибольшее значение из переданных. Вот как это работает:
n := min(10, 3, 22)
fmt.Println(m)
// 3
n := max(10, 3, 22)
fmt.Println(m)
// 22
Кроме того, эти функции могут принимать значения упорядоченных типов (ordered type): целые числа, вещественные числа или строки (а также производные от них):
x := min(9.99, 3.14, 5.27)
fmt.Println(x)
// 3.14
s := min("one", "two", "three")
fmt.Println(s)
// "one"
type ID int
id1 := ID(7)
id2 := ID(42)
id := max(id1, id2)
fmt.Println(id)
// 42
Функции принимают один или больше аргументов:
fmt.Println(min(10))
// 10
fmt.Println(min(10, 9))
// 9
fmt.Println(min(10, 9, 8))
// 8
// ..
В то же время они не являются вариационными. Если попробовать, то появится ошибка:
nums := []int{10, 9, 8}
n := min(nums...)
// Error: invalid operation: invalid use of ... with built-in min
Clear
Эта функция работает со срезами, картами и type parameter values. Вот как clear удаляет все элементы из карты:
m := map[string]int{"one": 1, "two": 2, "three": 3}
clear(m)
fmt.Printf("%#v\n", m)
// Output: map[string]int{}
С другой стороны, со срезами всё несколько иначе. Здесь функция обнуляет отдельные элементы без изменения длины:
s := []string{"one", "two", "three"}
clear(s)
fmt.Printf("%#v\n", s)
// []string{"", "", ""}
Clear не может изменить длину среза, но вполне в состоянии изменить значения элементов массива, который находится непосредственно под срезом. Ну а карта — указатель на структуру вида, поэтому противоречий в том, что clear удаляет элементы из карты, нет.
Ну и последнее — по поводу type parameter values. Вот как это работает:
func customClear [T []string | map[string]int] (container T) {
clear(container)
}
s := []string{"one", "two", "three"}
customClear(s)
fmt.Printf("%#v\n", s)
// []string{"", "", ""}
m := map[string]int{"one": 1, "two": 2, "three": 3}
customClear(m)
fmt.Printf("%#v\n", m)
// map[string]int{}
Получается, что customClear принимает аргумент container. Он может быть или срезом, или картой. А clear внутри функции занимается обработкой container в соответствии с типом: карты очищаются, а у срезов обнуляются элементы.
Важный момент — сlear не может работать с массивами.
arr := [3]int{1, 2, 3}
clear(arr)
// invalid argument: argument must be (or constrained by) map or slice
Вот полный список встроенных функций в Go 1.21:
appendr — добавляет значения в срез
Clear — удаляет или зануляет элементы контейнера
closer — закрывает канал
Complexr, real, imag — создают и разбирают комплексные числа
deleter — удаляет элемент карты по ключу
lenr — возвращает длину контейнера
capr — возвращает вместимость контейнера
maker — создаёт новый срез, карту или канал
newr — выделяет память под переменную
minr — выбирает минимальный из переданных аргументов
maxr — выбирает максимальный из переданных аргументов
Panicr и recover — создают и обрабатывают панику
print и println — печатают аргументы
Ну а на этом всё. Если вы уже работали с новой версией языка, расскажите, что понравилось, что нет, чего ещё ждёте.