Изучение лучших обновлений Go 1.21
Прошло две недели с момента выхода Go 1.21. Я потратил время на изучение новых функций этой версии. И я нахожу это потрясающим! В этом посте я хочу описать мои главные новые функции, которые мне нравятся больше всего.
Новый пакет слайсов
Если вы разрабатывали веб-сервисы с использованием Go, всегда наступает момент, когда вам нужно выполнить преобразование коллекций, фильтрацию, агрегацию и т. д. К сожалению, встроенной библиотеки, которую вы могли бы использовать, не было. Я предпочел использовать библиотеку github.com/elliotchance/pie/v2
:
В Go 1.21 базовая библиотека была расширена пакетом slices
:
Здесь существует множество методов. Давайте несколько из них! Один из методов, который вы можете использовать, — Contains
, который возвращает true
, если элемент присутствует в массиве; в противном случае false
. Давайте посмотрим пример:
package main import ( "fmt" "slices" ) func main() { arr := []int{1, 2, 3, 4, 5, 6, 7, 8} fmt.Printf("arr contains 5 = %t\n", slices.Contains(arr, 5)) // arr contains 5 = true fmt.Printf("arr contains 10 = %t\n", slices.Contains(arr, 10)) // arr contains 10 = false }
Этот метод работает только с массивами, элементы которых реализуют comparable
интерфейсы. Если вы хотите использовать этот метод с другими типами, вы можете использовать ContainsFunc
, который принимает функцию сравнения в качестве второго аргумента:
package main import ( "fmt" "slices" ) type Person struct { Name string Age int } func main() { arr := []Person{ {"John", 20}, {"Jane", 30}, {"Jack", 40}, } fmt.Printf("arr contains person {\"Jane\", 30}: %t\n", slices.ContainsFunc(arr, func(p Person) bool { return p.Name == "Jane" && p.Age == 30 })) // true fmt.Printf("arr contains person {\"Dima\", 27}: %t\n", slices.ContainsFunc(arr, func(p Person) bool { return p.Name == "Dima" && p.Age == 27 })) // false }
Еще один метод, который я считаю полезным, — Sort
. Опять же, эту функцию можно использовать только с массивом, элементы которого реализуют comparable
интерфейсов. Если вы хотите использовать его с другими типами, вам следует использовать SortFunc
с функцией сортировки, которая для элементов a
и b
возвращает отрицательное число, если a < b
, ноль, если a == b
, и положительное число, если a > b
.
Давайте посмотрим пример с фрагментом строк:
package main import ( "fmt" "slices" ) func main() { arr := []string{"orange", "apple", "banana", "grape", "mango"} fmt.Printf("before sorting: %v\n", arr) // before sorting: [orange apple banana grape mango] slices.Sort(arr) fmt.Printf("after sorting: %v\n", arr) // after sorting: [apple banana grape mango orange] }
Как видите, метод сортирует тот же фрагмент, который вы передаете методу. Если вы хотите сохранить исходный фрагмент несортированным, вы можете скопировать его в другой фрагмент, используя метод Clone
:
package main import ( "fmt" "slices" ) func main() { arr := []string{"orange", "apple", "banana", "grape", "mango"} copy := slices.Clone(arr) fmt.Printf("arr before sorting: %v\n", arr) // arr before sorting: [orange apple banana grape mango] fmt.Printf("copy before sorting: %v\n", copy) // copy before sorting: [orange apple banana grape mango] slices.Sort(copy) fmt.Printf("arr after sorting: %v\n", arr) // arr after sorting: [orange apple banana grape mango] fmt.Printf("copy after sorting: %v\n", copy) // copy after sorting: [apple banana grape mango orange] }
Тем не менее, в пакете отсутствуют методы для сопоставления срезов с другими типами, функции фильтрации, агрегации и т. д. Но это первый шаг, и я надеюсь, что позже пакет будет расширен дополнительными методами.
Новый пакет карт
Вместе с пакетом slices
был представлен пакет Go 1.21 maps
, предоставляющий некоторые методы для работы с картами. Он пока не такой богатый и имеет всего несколько функций.
Вы можете сравнить две карты с помощью функции Equal
или EqualFunc
, если ключ и/или значение не реализуют интерфейс comparable
. Вот пример:
package main import ( "fmt" "maps" ) func main() { m1 := map[string]int{"a": 1, "b": 2, "c": 3} m2 := map[string]int{"a": 1, "b": 2, "c": 3} m3 := map[string]int{"a": 1, "b": 2, "c": 4} fmt.Printf("m1 == m2: %t\n", maps.Equal(m1, m2)) // m1 == m2: true fmt.Printf("m1 == m3: %t\n", maps.Equal(m1, m3)) // m1 == m3: false }
Еще одна полезная функция — DeleteFunc
, которая удаляет элементы, если предикат, переданный в эту функцию, возвращает true
для пары ключ/значение. Вот код, который удаляет все четные значения:
package main import ( "fmt" "maps" ) func main() { m := map[string]int{"a": 1, "b": 2, "c": 3} maps.DeleteFunc(m, func(k string, v int) bool { return v%2 == 0 }) fmt.Println(m) // map[a:1 c:3] }
Как и пакет slices
, он все еще чистый и в нем отсутствует множество методов, которые могли бы пригодиться при разработке приложений, но это только первый выпуск.
Новая функция очистки
Новая функция clear
работает как с картами, так и со срезами. Для карт он удаляет все элементы. Для срезов он обнуляет все элементы. Вот пример:
package main import ( "fmt" ) func main() { s := []int{1, 2, 3, 4, 5} m := map[string]int{"a": 1, "b": 2, "c": 3} clear(s) clear(m) fmt.Println(s) // [0 0 0 0 0] fmt.Println(m) // map[] }
Новый пакет журналов и журналов
Ведение журнала является важной частью разработки веб-приложений. Иногда случаются плохие вещи, и вам хочется иметь возможность проследить, что произошло под капотом. Если вы раньше использовали пакет log
, он, вероятно, покажется вам относительно простым, поскольку он не поддерживает структурированное ведение журнала. Для этой цели я использовал go.uber.org/zap
:
В Go 1.21 был представлен новый пакет log/slog
, поддерживающий структурированное ведение журналов. Он также поддерживает уровни ведения журнала: «Отладка», «Информация», «Предупреждение» и «Ошибка».
Давайте посмотрим пример:
package main import ( "context" "log/slog" ) func main() { cxt := context.Background() slog.InfoContext(cxt, "Starting up...") slog.DebugContext(cxt, "Debugging...") a := 1 b := 2 slog.InfoContext(cxt, "Adding two numbers", "a", a, "b", b) c := a + b slog.InfoContext(cxt, "Result", "c", c) slog.InfoContext(cxt, "Shutting down...") }
В консоли вы увидите следующее:
2023/08/17 19:05:12 INFO Starting up... 2023/08/17 19:05:12 INFO Adding two numbers a=1 b=2 2023/08/17 19:05:12 INFO Result c=3 2023/08/17 19:05:12 INFO Shutting down...
Как видите, вы видите только журналы информационного уровня. Вы можете создать и настроить логгер самостоятельно:
package main import ( "context" "log/slog" "os" ) func main() { cxt := context.Background() l := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelDebug})) l.InfoContext(cxt, "Starting up...") l.DebugContext(cxt, "Debugging...") a := 1 b := 2 l.InfoContext(cxt, "Adding two numbers", "a", a, "b", b) c := a + b l.InfoContext(cxt, "Result", "c", c) l.InfoContext(cxt, "Shutting down...") }
Давайте посмотрим разницу:
time=2023-08-17T19:08:18.202+03:00 level=INFO msg="Starting up..." time=2023-08-17T19:08:18.204+03:00 level=DEBUG msg=Debugging... time=2023-08-17T19:08:18.204+03:00 level=INFO msg="Adding two numbers" a=1 b=2 time=2023-08-17T19:08:18.204+03:00 level=INFO msg=Result c=3 time=2023-08-17T19:08:18.204+03:00 level=INFO msg="Shutting down..."
Вы можете просматривать журналы в разных форматах, а также теперь отображаются журналы отладки. Также есть обработчик JSON, который вы можете использовать:
package main import ( "context" "log/slog" "os" ) func main() { cxt := context.Background() l := slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelDebug})) l.InfoContext(cxt, "Starting up...") l.DebugContext(cxt, "Debugging...") a := 1 b := 2 l.InfoContext(cxt, "Adding two numbers", "a", a, "b", b) c := a + b l.InfoContext(cxt, "Result", "c", c) l.InfoContext(cxt, "Shutting down...") }
Вот результат:
{"time":"2023-08-17T19:09:58.2505253+03:00","level":"INFO","msg":"Starting up..."} {"time":"2023-08-17T19:09:58.2515539+03:00","level":"DEBUG","msg":"Debugging..."} {"time":"2023-08-17T19:09:58.2521115+03:00","level":"INFO","msg":"Adding two numbers","a":1,"b":2} {"time":"2023-08-17T19:09:58.2521115+03:00","level":"INFO","msg":"Result","c":3} {"time":"2023-08-17T19:09:58.2521115+03:00","level":"INFO","msg":"Shutting down..."}
Я считаю это полезным, потому что теперь вы можете собирать структурированные журналы с помощью ELK, используя встроенную библиотеку.
Заключение
Команда Go с новым выпуском, все еще способная сохранить его минимальным. Go не перегружен множеством языковых конструкций, которые упрощают изучение языка. Это лучшее преимущество Го. Вам не нужно выбирать между альтернативами и способами что-то реализовать. Еще много нового в Go вы можете найти в разделе ниже. Здесь я только что описал основные функции, которые я бы использовал как разработчик. Надеюсь, вы найдете для себя что-нибудь полезное!
Спасибо, что дочитали до конца. Пожалуйста, подумайте о том, чтобы подписаться на автора и эту публикацию. Посетите Stackademic, чтобы узнать больше о том, как мы демократизируем бесплатное образование в области программирования во всем мире.