Проблема здесь в том, что shower
- это тип interface
. Типы интерфейсов в Go удерживают фактическое значение и его динамический тип. Подробнее об этом: Законы отражения # Представление интерфейса .
Выбранный фрагмент содержит 2 значения не nil
. Второе значение представляет собой значение интерфейса, пару (значение; тип), содержащее значение nil
и тип *display
. Цитирование из спецификации языка g1] Go: Операторы сравнения :
Значения интерфейса сопоставимы. Два значения интерфейса равны, если они имеют одинаковые динамические типы и равные динамические значения или оба имеют значение
blockquote>nil
.Поэтому, если вы сравните его с
nil
, это будетfalse
. Если вы сравните его со значением интерфейса, представляющим пару(nil;*display)
, это будетtrue
:if x == (*display)(nil) { panic("everything ok, nil found") }
Это кажется неосуществимым, так как вам нужно знать, какой тип интерфейса имеет.
Почему это реализовано таким образом?
Интерфейсы, в отличие от других конкретных типов (не-интерфейсы), могут содержать значения разных типов конкретных типов (разные статические типы). Время выполнения должно знать динамический или runtime-тип значения, хранящегося в переменной типа интерфейса.
interface
- это всего лишь набор методов, любой тип реализует его если одни и те же методы являются частью метода , заданного типа. Существуют типы, которые не могут бытьnil
, напримерstruct
или пользовательский тип сint
в качестве базового типа. В этих случаях вам не нужно будет сохранять значениеnil
этого конкретного типа.Но любой тип также включает конкретные типы, где
nil
является действительным (например, фрагменты, карты, каналы, все типы указателей), поэтому, чтобы сохранить значение во время выполнения, которое удовлетворяет интерфейсу, разумно поддерживать сохранениеnil
внутри интерфейса. Но помимоnil
внутри интерфейса мы должны хранить его динамический тип, поскольку значениеnil
не несет такую информацию. Альтернативным вариантом будет использованиеnil
в качестве самого значения интерфейса, когда значение, которое будет храниться в нем,nil
, но это решение недостаточно, так как оно потеряет информацию о динамическом типе.Некоторые говорят, что интерфейсы Go являются динамически типизированными, но это вводит в заблуждение. Они статически типизированы: переменная типа интерфейса всегда имеет один и тот же статический тип, и хотя во время выполнения значение, хранящееся в переменной интерфейса, может изменять тип, это значение всегда будет удовлетворять интерфейсу.
blockquote>В общем случае, если вы хотите указать
nil
для значения типаinterface
, используйте явное значениеnil
, а затем вы можете проверить равенствоnil
. Наиболее распространенным примером является встроенный типerror
, который является интерфейсом с одним методом. Всякий раз, когда нет ошибки, вы явно устанавливаете или возвращаете значениеnil
, а не значение какой-либо конкретной (неинтерфейсной) ошибки типа типа (что было бы очень плохой практикой, см. Демонстрацию ниже).В вашем примере путаница возникает из фактов, что:
- вы хотите иметь значение в качестве типа интерфейса (
shower
)- , но значение, которое вы хотите хранить в срезе не тип
shower
, а конкретный типПоэтому, когда вы помещаете
*display
тип в срезshower
, будет создано значение интерфейса, которое представляет собой пару (значение; тип), где значение равноnil
, а тип -*display
. Значение внутри пары будетnil
, а не значением самого интерфейса. Если вы поместите значениеnil
в срез, то значение интерфейса себя будетnil
, а условиемx == nil
будетtrue
.Демонстрация
См. этот пример: Игровая площадка
type MyErr string func (m MyErr) Error() string { return "big fail" } func doSomething(i int) error { switch i { default: return nil // This is the trivial true case case 1: var p *MyErr return p // This will be false case 2: return (*MyErr)(nil) // Same as case 1 case 3: var err error // Zero value is nil for the interface return err // This will be true because err is already interface type case 4: var p *MyErr return error(p) // This will be false because the interface points to a // nil item but is not nil itself. } } func main() { for i := 0; i <= 4; i++ { err := doSomething(i) fmt.Println(i, err, err == nil) } }
Выход:
0
true 1 false 2 false 3 true 4 false В случае 2 указатель
nil
но сначала он преобразуется в тип интерфейса (error
), поэтому создается значение интерфейса, которое содержит значениеnil
и тип*MyErr
, поэтому значение интерфейса не являетсяnil
.