Библиотеки Go — сокровищница ценных ресурсов, помогающих разрабатывать более качественные и быстрые приложения.
Перепробовав множество инструментов за свою карьеру, я убедился в том, что выбор правильных библиотек может стать “геймчейнджером” (переломным моментом) при устранении избыточного кода и улучшении общего качества ПО.
1. golang-module/carbon
Работа со временем в Go может быть довольно сложной задачей, особенно если полагаться только на встроенный пакет time.Time. Как указать значение года или месяца, используя time.Time в Go?
Раньше я часто сталкивался со сложным кодом, когда работал с временными значениями:
t := time.Now() // получение текущего времени
// изменение года на 2020
newTime := time.Date(2020, t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), t.Location())
Именно в таких ситуациях Carbon приходит на помощь, предлагая элегантное и эффективное решение, реализуемое с помощью всего одной короткой строки:
t := carbon.Now().SetYear(2020)
Carbon предлагает множество полезных утилит, которые делают работу со временем в Go намного удобнее, позволяя избежать проблем, связанных с обработкой объектов в time.Time.
Вот несколько практических примеров из документации Carbon на GitHub:
// Настоящее ли время?
carbon.Now().IsNow() // true
// Будущее ли время?
carbon.Tomorrow().IsFuture() // true
// Прошедшее ли время?
carbon.Yesterday().IsPast() // true
// Январь ли?
carbon.Parse("2020-08-05 13:14:15").IsJanuary() // false
// Понедельник ли?
carbon.Parse("2020-08-05 13:14:15").IsMonday() // false
carbon.Parse("2020-08-05 13:14:15").DiffForHumans() // настоящее время
carbon.Parse("2022-08-05 13:14:15").DiffForHumans() // 2 года спустя
2. samber/lo
Тому, кто часто работает со срезами, массивами или картами в Go-проектах, может пригодиться библиотека Samber/lo.
Эта библиотека создана в стиле Lodash с использованием новой функции-дженерика, реализованной в Go 1.18+. Samber/lo предоставляет широкий спектр полезных утилит, таких как filter
, map
, reduce
, count
и flatten
, чтобы упростить сложные задачи и сделать код более эффективным.
Вот пример того, как с помощью утилит библиотеки Samber/lo можно переписать Python-функцию range
в Go-стиле:
result := lo.Range(4)
// [0, 1, 2, 3]
result := lo.RangeFrom(1, 5)
// [1, 2, 3, 4, 5]
Вот несколько примеров из документации samber/lo на GitHub:
// Нахождение уникальных значений
names := lo.Uniq[string]([]string{"Samuel", "John", "Samuel"})
// []string{"Samuel", "John"}
// Фильтрация четных числе
even := lo.Filter[int]([]int{1, 2, 3, 4}, func(x int, index int) bool {
return x%2 == 0
})
// []int{2, 4}
// Перевертывание среза
reverseOrder := lo.Reverse[int]([]int{0, 1, 2, 3, 4, 5})
// []int{5, 4, 3, 2, 1, 0}
samber/lo не только предоставляет полезные утилиты для работы со срезами, массивами и картами, но и включает несколько удобных функций.
Например, функция для генерации случайной строки и функция для проверки того, была ли инициализирована структура:
// ---- RandomString
str := lo.RandomString(5, lo.LettersCharset)
// "eIGbt"
// ---- IsNotEmpty
type test struct {
foorbar string
}
lo.IsNotEmpty[test](test{foobar: ""})
// false
lo.IsNotEmpty[test](test{foobar: "foobar"})
// true
3. zerolog
При работе над большим проектом важно структурировать логи для удобства чтения и запросов, а также повышения производительности.
Zerolog — это библиотека логирования, которая отличается как высокой производительностью, так и невероятной простотой в использовании. Одно из моих ключевых правил: “Если ты перегружен множеством различных вариантов, не теряй времени — просто выбери один из них наугад и начни экспериментировать с ним методом проб и ошибок”.
Помимо впечатляющей производительности, Zerolog также предлагает широкий спектр полезных утилит.
Например, можно использовать Zerolog для регистрации положения, в котором было выдано событие лога, что облегчает отслеживание багов и проблем в вашем коде:
log.Logger = log.With().Caller().Logger()
log.Info().Msg("hello world")
// Вывод: {"level": "info", "message": "hello world",
// "caller": "/go/src/your_project/some_file:21"}
4. jinzhu/copier
Когда нужно выполнить копирование или глубокое клонирование, я сразу же вспоминаю об удобном инструменте Copier.
Он особенно полезен при работе со сложными структурами данных или вложенными структурами, когда ручное копирование данных может оказаться задачей утомительной и чреватой ошибками.
copier.CopyWithOption(&to, &from, copier.Option{IgnoreEmpty: true, DeepCopy: true})
Приведу пример. Допустим, есть две модели: User
, которая содержит всю пользовательскую информацию, и SimpleUser
, в которой нет таких пользовательских данных, как адрес электронной почты и пароль:
type User struct {
Name string
Age int
Email string
Password string
}
type SimpleUser struct {
Name string
Age int
}
Предположим, что нужно получить информацию о пользователе из базы данных, обработать ее и вернуть модифицированную версию объекта user
, не содержащую конфиденциальной информации, такой как адрес электронной почты и пароль:
// --- Мокап
func getUserFromDB() User {
return User{
Name: "Aiden", Age: 30,
Email: "[email protected]", Password: "thisisasecret"}
}
// ---
func Sample() (s SimpleUser, err error) {
user := getUserFromDB()
// ... какая-либо операция с user, затем возврат simpleUser
err = copier.Copy(&s, &user);
return s, err
}
Когда речь идет о создании глубоких копий структур, Copier становится незаменимым инструментом в арсенале разработчика (но нужно помнить, что он может не работать с неэкспортированными полями):
func Clone[T any](input T) (*T, error) {
var output = new(T)
err := copier.CopyWithOption(output, input, copier.Option{DeepCopy: true})
return output, err
}
Copier предлагает целый ряд возможностей:
- копирование из поля в поле с тем же именем;
- копирование из метода в поле с тем же именем;
- копирование из поля в метод с тем же именем;
- копирование из среза в срез;
- копирование из структуры в срез;
- копирование из карты в карту;
- принудительное копирование поля с меткой;
- игнорировать поле с меткой;
- глубокое копирование.
5. stretchr/testify
При тестировании на других языках, таких как C#, Javascript с Selenium и Playwright, используются утверждения и ожидания. Однако я не мог найти ничего подобного в Go, пока не обнаружил библиотеку Testify.
Пакет assert
Testify предоставляет множество полезных инструментов для тестирования. Например:
func TestSomething(t *testing.T) {
// утвердить равенство
assert.Equal(t, 123, 123, "they should be equal")
// утвердить неравенство
assert.NotEqual(t, 123, 456, "they should not be equal")
// утвердить для nil (хорошо подходит для ошибок)
assert.Nil(t, object)
// утвердить для not nil (хорошо подходит, когда вы чего-то ожидаете)
if assert.NotNil(t, object) {
// теперь, когда мы знаем, что объект не является nil, мы можем спокойно делать
// дальнейшие утверждения, не вызывая ошибок
assert.Equal(t, "Something", object.Value)
}
}
В версии testify v1 (на момент написания этой статьи команда Testify работала над версией v2) библиотека предоставляет несколько функций для облегчения тестирования в Go.
- Пакет Asserts, упомянутый выше.
- Пакет Mock, предлагающий простой способ создания mock-объектов, которые могут заменить реальные объекты во время тестирования.
- Пакет Suite, позволяющий создать набор тестов с помощью определенной структуры, определить методы
setup
/teradown
и тестирования в этой структуре, а также запустить их с помощьюgo test
как обычно.
Это лишь несколько библиотек, которые могут оказаться невероятно полезными при работе над проектами Go. Всегда помните о необходимости повышения производительности.
Читайте также:
- Почему вам стоит написать свой API-шлюз с нуля
- Конкурентность на Go: объяснение шаблона Worker Pool
- Бесперебойный API на Golang
Читайте нас в Telegram, VK и Дзен
Перевод статьи Aiden (@func25): Go Libraries I Use in Nearly Every Project