2.9Kпросмотров
22 июля 2025 г.
📷 ФотоScore: 3.2K
🎨 Interface Comparisons В этот раз поговорим про сравнения интерфейсов и развеем все мифы, которые вы могли встретить на своем пути Нюансы в этой теме важны для понимания ввиду возможности паник при использовании некорректных нижележащих сущностей Для начала взглянем на структуру не пустого интерфейса:
type iface struct { tab *itab data unsafe.Pointer
}
— С полем data все прозрачно - это непосредственно те данные, которые мы закладываем при присваивании интерфейсу какого-то значения
— itab содержит метаданные об интерфейсе, о реализующем его типе и методах Ключевое: при сравнении не пустых интерфейсов мы сравниваем две структуры iface: itab и data. В случае с пустыми интерфейсами eface сравниваем не itab, а сам тип _type Приступим к рассмотрению боевых примеров! 1. Сравнение простых композитных типов
В данном случае все достаточно тривиально, itab одинаковы и, следовательно можем приступить к сравнению data, а они как раз являются разнымии. Смело получаем false и идем дальше
func A() { var v1, v2 any p1 := Person{ Name: "Bob", Age: 18, } p2 := Person{ Name: "John", Age: 19, } v1, v2 = p1, p2 fmt.Println(v1 == v2)
} 2. Несравнимые типы
Первое, что необходимо зарубить себе на носу - такие сущности как map, slice, func являются uncomparable. При попытке сравнить таковые мы моментально получаем panic и идем в люльку (в близлежащий defer) Go Playground (тык) 3. Композитные типы с вложенными несравнимыми сущностями
Нос мы свой не щадим и зарубаем еще одну штуку - несравнимый тип, содержащийся в структуре, аффектит сравнение всей структуры. Иначе говоря, все поля структуры (включая вложенные) должны быть сравнимыми
type UncomparablePerson struct { Name string Age uint8 Pets []string Things map[string]struct{} Action func()
} 3.1 Различные типы и вложенно несравнимые сущности Мы дошли до нетривиального случая. Имеем следующий расклад:
— Наши _type разные, ввиду того, что типы разные
— Один из типов вложенно хранит в себе множество несравнимых типов Наивным предположением было бы думать, что мы словим панику, но как бы не так! Сравнение двух структур (eface или iface) происходит линейно (как и с массивами): сначала сравниваются поля (tab или _type), потом data. Так как типы у нас разные, поэтому сравнения полей data не будет Получаем заслуженный false и идем дальше!
func C() { var v1, v2 any p := Person{ Name: "Bob", Age: 18, } up := UncomparablePerson{ Name: "John", Age: 21, Pets: nil, Things: map[string]struct{}{ "ball": {}, }, } up.Action = func() { fmt.Printf("My age is %s\n", up.Name) } v1, v2 = p, up fmt.Println(v1 == v2) // false
} 3.2. Одинаковые типы и вложенно несравнимые сущности Комментарий с выводом говорит сам за себя. Типы одинаковые, data содержит несравнимые сущности, получаем panic. Все тривиально Go Playground(тык) 4. Сравнение интерфейса напрямую с неинтерфейсным значением
Мало тех, кто знает о том, что можно сравнить переменную интерфейса напрямую с каким-то значением. Происходить все будет аналогично сравнению двух интерфейсов
func E() { var v1 any p1 := Person{ Name: "A", Age: 12, } v1 = p1 fmt.Println(v1 == p1)
} 5. Алиасы и новые типы в интерфейсах
Напоминание о том, как работают синонимы и типовые переопределения
type ( AliasedInteger = int // Синоним (type alias) для int:идентичен int, взаимозаменяем с ним AnotherInteger int // Новый тип на базе int: не совместим с int напрямую, но имеет ту же внутреннюю структуру
) func F() { var v1, v2 any i, ai, ali := 1, AnotherInteger(1), AliasedInteger(1) v1, v2 = i, ali fmt.Println(v1 == v2) // true v1, v2 = i, ai fmt.Println(v1 == v2) // false castAiToInt := int(ai) v1, v2 = i, castAiToInt fmt.Println(v1 == v2) // true
} Надеемся, что материал для вас был полезным. Если возникли какие-ни