Как в Go происходит сравнение интерфейсов и в каких случаях оно возможно?

Ответ

Сравнение интерфейсов в Go — это важная концепция, которая проверяет как динамический тип, так и динамическое значение, хранящиеся в интерфейсе.

Правила сравнения

Два интерфейсных значения равны, если выполняется одно из условий:

  1. Оба интерфейса равны nil.
  2. Их динамические типы идентичны, и их динамические значения равны в соответствии с правилами сравнения для этого типа.

Интерфейсы не равны, если:

  • Один из них nil, а другой нет.
  • Их динамические типы различаются.
  • Их типы одинаковы, но значения не равны.

Пример сравнения

var a, b interface{}

a = 100
b = 100
fmt.Println(a == b) // true (типы int, значения 100 == 100)

b = int64(100)
fmt.Println(a == b) // false (типы разные: int != int64)

var c interface{} = "hello"
var d interface{} = "hello"
fmt.Println(c == d) // true (типы string, значения равны)

⚠️ Паника при сравнении несравниваемых типов

Попытка сравнить два интерфейса, которые содержат значения несравниваемых типов, вызовет панику во время выполнения.

К несравниваемым типам относятся:

  • Слайсы (slice)
  • Карты (map)
  • Функции (func)
var c interface{} = []int{1, 2}
var d interface{} = []int{1, 2}

// fmt.Println(c == d) // паника: runtime error: comparing uncomparable type []int

Классическая ловушка: nil интерфейс vs интерфейс с nil значением

Интерфейс не равен nil, если в нем хранится значение, даже если само это значение является nil-указателем. Это происходит потому, что у такого интерфейса есть динамический тип.

type MyError struct{}
func (e *MyError) Error() string { return "error" }

func getError() error {
    var err *MyError = nil // Создаем nil-указатель на структуру
    return err             // Возвращаем его как интерфейс error
}

func main() {
    err := getError()
    // err не равен nil, т.к. он содержит тип (*MyError) и значение (nil)
    if err != nil {
        fmt.Printf("Ошибка! Тип: %T, Значение: %vn", err, err)
    }
}
// Вывод:
// Ошибка! Тип: *main.MyError, Значение: <nil>

Ответ 18+ 🔞

А, интерфейсы в Go, ёпта! Ну это ж классика, блядь, вечный источник головной боли и паники в три часа ночи. Слушай, тут всё просто, как два пальца об асфальт, но с подвохом, как всегда.

Вот представь: интерфейс — это такая коробка, блядь. Внутри может лежать что угодно: число, строка, твоя старая фотка. И сравнивать эти коробки — это не просто так, на глазок.

Правила, сука, железные: Коробки считаются одинаковыми, если:

  1. Обе пустые, нихуя в них нет, обе nil. Всё, вопросов нет.
  2. Внутри лежат одинаковые хуйни (один и тот же тип) и эти самые хуйни равны между собой по своим внутренним законам.

А вот если:

  • Одна коробка пустая, а в другой что-то лежит — нихуя не равны.
  • Типы внутри разные — нихуя не равны.
  • Типы одинаковые, а значения разные — нихуя не равны.
var a, b interface{}

a = 100
b = 100
fmt.Println(a == b) // true (оба int, оба 100, всё ок)

b = int64(100)
fmt.Println(a == b) // false (внутри разная хуйня: int vs int64, ёпта!)

var c interface{} = "hello"
var d interface{} = "hello"
fmt.Println(c == d) // true (оба string, оба "hello")

А теперь главный пиздец, блядь!

Если ты сунешь в эту коробку несравниваемую хуйню, а потом попробуешь коробки сравнить — тебе пизда, паника на ровном месте! Кто несравниваемый? Да всякие извращенцы: слайсы, мапы, функции!

var c interface{} = []int{1, 2}
var d interface{} = []int{1, 2}

// fmt.Println(c == d) // ПИЗДЕЦ! Паника! Слайсы сравнивать низяяя!

И ловушка года, блядь, классика жанра!

nil интерфейс и интерфейс с nil значением — это, сука, две большие разницы! Представь, у тебя есть коробка, в которой лежит бумажка с надписью "ничего". Коробка-то не пустая! В ней же бумажка лежит, ёбана!

type MyError struct{}
func (e *MyError) Error() string { return "error" }

func getError() error {
    var err *MyError = nil // Указатель на структуру, который нихуя не указывает (nil)
    return err             // Возвращаем его как интерфейс error
}

func main() {
    err := getError()
    // Сюрприз, сука! err тут НЕ nil!
    // Потому что коробка (интерфейс error) НЕ ПУСТАЯ.
    // В ней лежит информация: "тут внутри тип *MyError, а значение у него — nil".
    if err != nil {
        fmt.Printf("Ошибка! Тип: %T, Значение: %vn", err, err) // Вот же ж блядь!
    }
}
// Вывод:
// Ошибка! Тип: *main.MyError, Значение: <nil>

Вот так вот, ебать мои старые костыли. Кажется, что всё просто, а потом бац — и ты уже дебажишь эту хуйню до утра. Главное запомни: коробка с бумажкой "ничего" — это не пустая коробка!