Библиотеки 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. Всегда помните о необходимости повышения производительности.

Читайте также:

Читайте нас в TelegramVK и Дзен


Перевод статьи Aiden (@func25): Go Libraries I Use in Nearly Every Project

Предыдущая статьяЧто такое Next.js App Router и готов ли он к использованию в производстве
Следующая статьяПрактическое применение KSP