Что такое определение нового типа и псевдоним типа (type alias) в Go? В чем разница?

Ответ

В Go есть два похожих, но принципиально разных механизма работы с типами: определение нового типа и создание псевдонима типа.

1. Определение нового типа (Type Definition)

Это основной способ создания собственных типов. Он использует синтаксис type NewType UnderlyingType.

  • Создается новый, отдельный тип.
  • Новый тип не является взаимозаменяемым с его базовым типом без явного преобразования.
  • Это позволяет компилятору обеспечивать типобезопасность и улучшает читаемость кода, добавляя семантический смысл.

Пример:

// Определяем два новых типа на основе int
type UserID int
type ProductID int

func GetUser(id UserID) {
    fmt.Printf("Получение пользователя с ID: %dn", id)
}

func main() {
    var uid UserID = 42
    var pid ProductID = 100

    GetUser(uid) // OK

    // GetUser(pid) // Ошибка компиляции: cannot use pid (type ProductID) as type UserID in argument to GetUser
    // GetUser(123) // Ошибка компиляции: cannot use 123 (type int) as type UserID in argument to GetUser

    // Необходимо явное преобразование типа
    var regularInt int = 123
    GetUser(UserID(regularInt)) // OK
}

Здесь UserID и ProductID — это два разных типа, хотя оба основаны на int.

2. Псевдоним типа (Type Alias)

Этот механизм был добавлен в Go 1.9. Он использует синтаксис type Alias = OriginalType.

  • Не создает новый тип, а лишь вводит для него второе имя (псевдоним).
  • Псевдоним и оригинальный тип полностью взаимозаменяемы.
  • Основное предназначение — постепенный рефакторинг большого объема кода, когда нужно переименовать тип, не ломая совместимость.

Пример:

// Определяем тип
type UserID int

// Создаем псевдоним для UserID. Теперь `LocalUserID` и `UserID` - это одно и то же.
type LocalUserID = UserID

func GetUser(id UserID) {
    fmt.Printf("Получение пользователя с ID: %dn", id)
}

func main() {
    var localId LocalUserID = 42

    // Псевдоним можно использовать напрямую, без преобразования
    GetUser(localId) // OK

    var uid UserID = localId // OK, типы идентичны
    fmt.Println(uid)
}

Итог:

  • Определение типа (type T1 T2) — для создания семантически разных типов и повышения типобезопасности.
  • Псевдоним типа (type T1 = T2) — для рефакторинга и совместимости, когда нужно, чтобы два имени обозначали один и тот же тип.