Отличие uint от int?go-23

Основные различия

1. Диапазон значений

int - знаковое целое число:

var i int = -42  // Может быть отрицательным

uint - беззнаковое целое число:

var u uint = 42  // Только положительные значения

2. Размер в памяти

Размер обоих типов зависит от платформы:

  • 32-битные системы: 32 бита (4 байта)
  • 64-битные системы: 64 бита (8 байта)

Но диапазоны отличаются:
Тип 32-битный диапазон 64-битный диапазон
int -2³¹ до 2³¹-1 (-2.1B до 2.1B) -2⁶³ до 2⁶³-1
uint 0 до 2³²-1 (0 до 4.2B) 0 до 2⁶⁴-1

3. Использование в операциях

Проблема смешивания типов:

var i int = -5
var u uint = 10
sum := i + u  // Ошибка компиляции!

Требуется явное приведение:

sum := i + int(u)  // Корректно

4. Переполнение

int при переполнении "заворачивается" в отрицательные:

var i int = math.MaxInt
i++  // Станет math.MinInt

uint при переполнении "заворачивается" в 0:

var u uint = math.MaxUint
u++  // Станет 0

Когда что использовать

Используйте uint для:

  1. Битовых операций
  2. Индексов, когда отрицательные значения невозможны
  3. Работы с бинарными данными
  4. Системного программирования (адреса памяти)

Пример:

func countBits(n uint) int {
    count := 0
    for n != 0 {
        count++
        n &= n - 1
    }
    return count
}

Используйте int для:

  1. Математических операций, где возможны отрицательные числа
  2. Индексов в большинстве случаев (идиоматично для Go)
  3. Общих вычислений

Пример:

func abs(n int) int {
    if n < 0 {
        return -n
    }
    return n
}

Особенности производительности

  1. На большинстве современных процессоров нет разницы в скорости
  2. В некоторых случаях uint может быть быстрее для битовых операций
  3. int обычно предпочтительнее для индексов (даже когда uint логичнее)

Подводные камни

  1. Неожиданные результаты при приведении отрицательных int к uint:
i := -1
u := uint(i)  // 18446744073709551615 (на 64-бит)
  1. Сравнения между разными типами требуют осторожности:
if uint(-1) > 0 {  // Всегда true!
    fmt.Println("Unexpected!")
}
  1. JSON unmarshalling может вести себя по-разному для uint и int.

Резюмируем

  1. int - знаковый целочисленный тип, uint - беззнаковый
  2. Оба имеют платформозависимый размер (32/64 бита)
  3. uint не может хранить отрицательные значения
  4. Смешивание типов требует явного приведения
  5. Переполнение обрабатывается по-разному
  6. int идиоматичнее для большинства случаев в Go
  7. uint полезен для битовых операций и низкоуровневых задач
  8. Требуется осторожность при преобразованиях и сравнениях

Правила выбора:

  • По умолчанию используйте int
  • Для битовых операций и индексов без отрицательных значений - uint
  • Избегайте смешивания в выражениях без явного приведения