Как сгенерировать UUID в Go? Нет встроенной функции?
Правильный способ: используйте пакет github.com/google/uuid
В Go нет встроенной функции для генерации UUID в стандартной библиотеке (и, скорее всего, никогда не будет — разработчики Go предпочитают держать ядро минимальным). Однако существует официально рекомендованный и самый популярный пакет от Google — github.com/google/uuid. Он полностью соответствует RFC 4122, поддерживает все версии UUID (1, 3, 4, 5) и является де-факто стандартом в Go-сообществе.
Установка
go get github.com/google/uuid
Пример: генерация UUID v4 (случайный)
UUID v4 — самый распространённый тип для большинства задач. Он генерируется из криптостойкого случайного источника:
package main
import (
"fmt"
"github.com/google/uuid"
)
func main() {
// Генерация одного UUID
id := uuid.New()
fmt.Println(id.String()) // например: f47ac10b-58cc-4372-a567-0e02b2c3d479
// Или сразу как строку
fmt.Println(uuid.New().String())
}
Другие версии UUID
- UUID v1 (на основе времени и MAC-адреса):
uuid.NewUUID() - UUID v3 (на основе MD5-хеша от namespace и имени):
uuid.NewMD5(uuid.NameSpaceDNS, []byte("example.com")) - UUID v5 (на основе SHA1-хеша):
uuid.NewSHA1(uuid.NameSpaceURL, []byte("https://example.com"))
Парсинг существующего UUID
id, err := uuid.Parse("f47ac10b-58cc-4372-a567-0e02b2c3d479")
if err != nil {
panic(err)
}
fmt.Println(id.Version()) // 4
Почему ваша самодельная реализация плоха?
Ваш код имеет две проблемы:
- Не устанавливаются биты версии и варианта. В UUID v4 7-й байт должен иметь старшие 4 бита равные
0100(версия 4), а 9-й байт — старшие 2 бита равные10(вариант RFC 4122). Без этого ваш UUID не соответствует стандарту и может быть не принят другими системами. - Форматирование через
fmt.Sprintfнеэффективно при массовой генерации — создаётся много временных строк.
Пакет google/uuid делает всё правильно и эффективно. Он использует crypto/rand под капотом, но самостоятельно выставляет нужные биты. Кроме того, он предоставляет тип uuid.UUID, который можно сериализовать в JSON, хранить в базе данных как строку или 16-байтовый массив.
Альтернативы (если не хотите внешних зависимостей)
Если вам критично не использовать сторонние пакеты (например, в учебных целях или микросервисе с жёсткими ограничениями), можно написать корректную реализацию самостоятельно:
package main
import (
"crypto/rand"
"fmt"
)
func main() {
uuid := make([]byte, 16)
_, _ = rand.Read(uuid)
// Устанавливаем версию 4 (биты 4-7 в 7-м байте)
uuid[6] = (uuid[6] & 0x0f) | 0x40
// Устанавливаем вариант RFC 4122 (биты 6-7 в 9-м байте)
uuid[8] = (uuid[8] & 0x3f) | 0x80
// Форматируем в стандартный вид
fmt.Printf("%08x-%04x-%04x-%04x-%012x\n",
uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:16])
}
Но я настоятельно рекомендую использовать готовую библиотеку — она протестирована, оптимизирована и избавит вас от головной боли с edge cases.
<<Пакет github.com/satori/go.uuid (альтернатива)
Если по какой-то причине пакет от Google вам не подходит (например, вы используете старую версию Go или нужна поддержка UUID v6/v7/v8), обратите внимание на github.com/satori/go.uuid. Он тоже очень популярен (более 5k звезд на GitHub) и предоставляет схожий функционал.
Пример использования
package main
import (
"fmt"
"github.com/satori/go.uuid"
)
func main() {
// UUID v4
id := uuid.NewV4()
fmt.Println(id.String())
// UUID v1
id1, _ := uuid.NewV1()
fmt.Println(id1.String())
// Парсинг
parsed, err := uuid.FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
if err != nil {
panic(err)
}
fmt.Println(parsed.Version())
}
Различия между пакетами
- Google UUID — более современный, активно поддерживается Google, использует
crypto/randпо умолчанию, имеет методuuid.Nilдля пустого UUID. - Satori UUID — старше, но всё ещё работает. Некоторые разработчики отмечают, что он менее производительный при массовой генерации.
Оба пакета решают вашу задачу. Выбирайте тот, который вам удобнее по API. Лично я рекомендую Google UUID как более современный стандарт.
Ответов пока нет.
Войдите чтобы оставить ответ