uuid для Go: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Библиотека uuid для Go: полное руководство по генерации UUID

1. Что это и зачем нужна

UUID (Universally Unique Identifier) — это стандарт идентификации, используемый в разработке программного обеспечения для создания уникальных идентификаторов без необходимости централизованного координатора. В экосистеме Go библиотека github.com/google/uuid является де-факто стандартом для работы с UUID. Она предоставляет надежные и производительные инструменты для генерации, парсинга и сравнения UUID различных версий.

Зачем нужна эта библиотека? В современных распределенных системах, микросервисной архитектуре и базах данных часто требуется создавать уникальные идентификаторы, которые не будут конфликтовать между разными узлами системы. UUID решает эту проблему: вы можете генерировать идентификаторы на клиенте, сервере или в любом другом месте, не опасаясь коллизий. Библиотека google/uuid поддерживает UUID версий 1 (на основе времени и MAC-адреса), 4 (случайные) и 5 (на основе хеша SHA-1).

Библиотека активно используется в таких проектах, как Kubernetes, Docker, CockroachDB и многих других. Она оптимизирована для высокой производительности, поддерживает потокобезопасную генерацию и предоставляет удобный API для всех основных операций с UUID.

2. Установка

Для установки библиотеки выполните команду в терминале:

go get github.com/google/uuid

После этого импортируйте пакет в своем коде:

import "github.com/google/uuid"

Убедитесь, что у вас установлен Go версии 1.16 или выше. Если вы используете Go modules (рекомендуется), библиотека автоматически добавится в ваш go.mod и go.sum файлы.

3. Быстрый старт — минимальный рабочий пример

Вот самый простой пример генерации UUID версии 4 (случайный):

package main

import (
    "fmt"
    "github.com/google/uuid"
)

func main() {
    // Генерируем новый UUID версии 4
    id := uuid.New()
    fmt.Println("Сгенерированный UUID:", id.String())
    
    // Проверяем, что это действительно UUID
    fmt.Println("Тип UUID:", id.Version())
    fmt.Println("Вариант UUID:", id.Variant())
}

Вывод программы будет выглядеть примерно так:

Сгенерированный UUID: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
Тип UUID: 4
Вариант: RFC 4122

4. Основные методы и функции

4.1. uuid.New()

Сигнатура: func New() UUID

Описание: Генерирует новый UUID версии 4 (случайный). Это самый популярный метод для создания уникальных идентификаторов. Использует криптостойкий генератор случайных чисел.

package main

import (
    "fmt"
    "github.com/google/uuid"
)

func main() {
    id := uuid.New()
    fmt.Printf("UUID: %s\n", id.String())
    fmt.Printf("Версия: %d\n", id.Version())
    fmt.Printf("Вариант: %d\n", id.Variant())
}

4.2. uuid.NewUUID()

Сигнатура: func NewUUID() (UUID, error)

Описание: Генерирует UUID версии 1 на основе текущего времени и MAC-адреса. Может вернуть ошибку, если не удалось получить MAC-адрес.

package main

import (
    "fmt"
    "log"
    "github.com/google/uuid"
)

func main() {
    id, err := uuid.NewUUID()
    if err != nil {
        log.Fatal("Ошибка генерации UUID:", err)
    }
    fmt.Printf("UUID v1: %s\n", id.String())
    fmt.Printf("Время создания: %v\n", id.Time())
    fmt.Printf("MAC-адрес: %x\n", id.Node())
}

4.3. uuid.NewRandom()

Сигнатура: func NewRandom() (UUID, error)

Описание: Аналог New(), но возвращает ошибку, если не удалось получить случайные байты. Позволяет обработать ошибку генерации.

package main

import (
    "fmt"
    "log"
    "github.com/google/uuid"
)

func main() {
    id, err := uuid.NewRandom()
    if err != nil {
        log.Fatal("Ошибка генерации случайного UUID:", err)
    }
    fmt.Printf("Случайный UUID: %s\n", id.String())
}

4.4. uuid.NewSHA1()

Сигнатура: func NewSHA1(space UUID, data []byte) UUID

Описание: Создает UUID версии 5 на основе SHA-1 хеша от пространства имен и данных. Гарантирует, что одинаковые входные данные дадут одинаковый UUID.

package main

import (
    "fmt"
    "github.com/google/uuid"
)

func main() {
    // Используем стандартное пространство имен DNS
    space := uuid.NameSpaceDNS
    data := []byte("example.com")
    
    id := uuid.NewSHA1(space, data)
    fmt.Printf("UUID v5 для example.com: %s\n", id.String())
    
    // Повторный вызов даст тот же UUID
    id2 := uuid.NewSHA1(space, data)
    fmt.Printf("Повторный вызов: %s\n", id2.String())
    fmt.Printf("UUID совпадают: %v\n", id == id2)
}

4.5. uuid.Parse()

Сигнатура: func Parse(s string) (UUID, error)

Описание: Парсит строку в UUID. Поддерживает различные форматы: с дефисами, без дефисов, с фигурными скобками.

package main

import (
    "fmt"
    "log"
    "github.com/google/uuid"
)

func main() {
    // Разные форматы строк UUID
    inputs := []string{
        "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
        "6ba7b8109dad11d180b400c04fd430c8",
        "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}",
    }
    
    for _, input := range inputs {
        id, err := uuid.Parse(input)
        if err != nil {
            log.Printf("Ошибка парсинга '%s': %v", input, err)
            continue
        }
        fmt.Printf("Распарсено: '%s' -> %s\n", input, id.String())
    }
}

4.6. uuid.Must()

Сигнатура: func Must(uuid UUID, err error) UUID

Описание: Утилита для случаев, когда ошибка невозможна или должна вызвать панику. Часто используется с Parse для констант.

package main

import (
    "fmt"
    "github.com/google/uuid"
)

func main() {
    // Используем Must для константных UUID
    id := uuid.Must(uuid.Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8"))
    fmt.Printf("UUID: %s\n", id.String())
    
    // Must также работает с NewRandom, но это опасно
    id2 := uuid.Must(uuid.NewRandom())
    fmt.Printf("Случайный UUID: %s\n", id2.String())
}

4.7. uuid.ClockSequence()

Сигнатура: func (u UUID) ClockSequence() int

Описание: Возвращает номер последовательности часов для UUID версии 1. Для других версий возвращает 0.

package main

import (
    "fmt"
    "log"
    "github.com/google/uuid"
)

func main() {
    id, err := uuid.NewUUID()
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("Clock sequence: %d\n", id.ClockSequence())
    fmt.Printf("Версия: %d\n", id.Version())
}

4.8. uuid.Node()

Сигнатура: func (u UUID) Node() []byte

Описание: Возвращает MAC-адрес узла для UUID версии 1. Для других версий возвращает nil.

package main

import (
    "fmt"
    "log"
    "github.com/google/uuid"
)

func main() {
    id, err := uuid.NewUUID()
    if err != nil {
        log.Fatal(err)
    }
    
    node := id.Node()
    fmt.Printf("MAC-адрес: %02x:%02x:%02x:%02x:%02x:%02x\n",
        node[0], node[1], node[2], node[3], node[4], node[5])
}

4.9. uuid.Time()

Сигнатура: func (u UUID) Time() Time

Описание: Возвращает время создания для UUID версии 1. Возвращает нулевое время для других версий.

package main

import (
    "fmt"
    "log"
    "time"
    "github.com/google/uuid"
)

func main() {
    id, err := uuid.NewUUID()
    if err != nil {
        log.Fatal(err)
    }
    
    uuidTime := id.Time()
    goTime := time.Time(uuidTime)
    fmt.Printf("Время создания UUID: %s\n", goTime.Format(time.RFC3339))
    fmt.Printf("Прошло с момента создания: %v\n", time.Since(goTime))
}

4.10. uuid.Version()

Сигнатура: func (u UUID) Version() int

Описание: Возвращает версию UUID (1, 4 или 5). Полезно для проверки типа UUID.

package main

import (
    "fmt"
    "github.com/google/uuid"
)

func main() {
    uuids := []uuid.UUID{
        uuid.New(),
        uuid.Must(uuid.NewUUID()),
        uuid.NewSHA1(uuid.NameSpaceDNS, []byte("test")),
    }
    
    for _, id := range uuids {
        fmt.Printf("UUID: %s, Версия: %d\n", id.String(), id.Version())
    }
}

5. Реальный практический пример

Рассмотрим реальный пример — систему управления пользователями, где UUID используются для идентификации пользователей, сессий и токенов.

package main

import (
    "crypto/sha256"
    "encoding/hex"
    "fmt"
    "log"
    "sync"
    "time"
    
    "github.com/google/uuid"
)

// User представляет пользователя системы
type User struct {
    ID        uuid.UUID
    Email     string
    CreatedAt time.Time
    UpdatedAt time.Time
}

// Session представляет сессию пользователя
type Session struct {
    ID        uuid.UUID
    UserID    uuid.UUID
    Token     string
    ExpiresAt time.Time
}

// UserStore — потокобезопасное хранилище пользователей
type UserStore struct {
    mu       sync.RWMutex
    users    map[uuid.UUID]*User
    sessions map[string]*Session
}

// NewUserStore создает новое хранилище
func NewUserStore() *UserStore {
    return &UserStore{
        users:    make(map[uuid.UUID]*User),
        sessions: make(map[string]*Session),
    }
}

// CreateUser создает нового пользователя
func (s *UserStore) CreateUser(email string) (*User, error) {
    s.mu.Lock()
    defer s.mu.Unlock()
    
    // Проверяем, что email уникален
    for _, user := range s.users {
        if user.Email == email {
            return nil, fmt.Errorf("пользователь с email %s уже существует", email)
        }
    }
    
    user := &User{
        ID:        uuid.New(),
        Email:     email,
        CreatedAt: time.Now(),
        UpdatedAt: time.Now(),
    }
    
    s.users[user.ID] = user
    return user, nil
}

// GetUser возвращает пользователя по ID
func (s *UserStore) GetUser(id uuid.UUID) (*User, error) {
    s.mu.RLock()
    defer s.mu.RUnlock()
    
    user, exists := s.users[id]
    if !exists {
        return nil, fmt.Errorf("пользователь с ID %s не найден", id)
    }
    return user, nil
}

// CreateSession создает новую сессию для пользователя
func (s *UserStore) CreateSession(userID uuid.UUID) (*Session, error) {
    s.mu.Lock()
    defer s.mu.Unlock()
    
    // Проверяем существование пользователя
    if _, exists := s.users[userID]; !exists {
        return nil, fmt.Errorf("пользователь не найден")
    }
    
    // Генерируем токен на основе UUID и времени
    tokenData := fmt.Sprintf("%s:%d", uuid.New().String(), time.Now().UnixNano())
    hash := sha256.Sum256([]byte(tokenData))
    token := hex.EncodeToString(hash[:])
    
    session := &Session{
        ID:        uuid.New(),
        UserID:    userID,
        Token:     token,
        ExpiresAt: time.Now().Add(24 * time.Hour), // Сессия на 24 часа
    }
    
    s.sessions[session.Token] = session
    return session, nil
}

// ValidateSession проверяет валидность сессии
func (s *UserStore) ValidateSession(token string) (*Session, error) {
    s.mu.RLock()
    defer s.mu.RUnlock()
    
    session, exists := s.sessions[token]
    if !exists {
        return nil, fmt.Errorf("сессия не найдена")
    }
    
    if time.Now().After(session.ExpiresAt) {
        return nil, fmt.Errorf("сессия истекла")
    }
    
    return session, nil
}

// GenerateAPIKey генерирует API ключ на основе UUID v5
func GenerateAPIKey(userID uuid.UUID, service string) uuid.UUID {
    // Используем пространство имен для API ключей
    namespace := uuid.Must(uuid.Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8"))
    data := []byte(fmt.Sprintf("%s:%s", userID.String(), service))
    return uuid.NewSHA1(namespace,                    

Рекомендаций