Представьте себе Any
и Optional<Wrapped>
как коробки.
Any
- это непрозрачная коробка, которая может содержать что угодно . Вы можете пролить свет на то, что внутри, пытаясь разыграть его, используя as?
. Optional<Wrapped>
- прозрачный ящик. Он позволяет увидеть, что его содержимое имеет значение Optional<Wrapped>.some(wrapped)
или Optional<Wrapped>.none()
.
Optional<Any>
и Any
, содержащие Optional<Wrapped>
, не одно и то же.
1. x
- это значение Optional<Any>.none
, иначе nil
. nil
печатается, так что это имеет смысл.
2. type(of: x)
- это тип x
, Optional<Any>
, как мы и ожидали.
3. x == nil
вызывает оператор ==
типа (T, T) -> Bool
. Поскольку оба аргумента должны быть одного типа, и поскольку x
имеет значение Optional<Any>.none
(типа Optional<Any>
), nil
определяется как Optional<Any>.none
. Эти два значения эквивалентны, поэтому мы получаем true
, как и ожидалось.
4. y
- это значение типа Any
. Во время компиляции больше ничего не известно о y
. Так уж получилось, что значение, скрытое непрозрачной шторкой Any
, составляет x
. print
реализован, чтобы "видеть сквозь" Any
instances
, чтобы показать вам, что на самом деле там.
5. type(of: y)
делает что-то похожее на print
. Он использует информацию о типе времени выполнения, чтобы точно определить, какое значение времени выполнения хранится в y
. Тип *1039* stati *1168* равен Any
, но type(of: y)
показывает нам, что тип времени выполнения его значения равен Optional<Any>
.
6. y == nil
Здесь немного сложнее.
Выбор перегрузки вызываемой функции или оператора выполняется во время компиляции на основе статически известных типов операнды. То есть операторы и функции не отправляются динамически в зависимости от типов их аргументов, от способа их динамической отправки в зависимости от типа их объектов (например, foo
in foo.bar()
).
Выбранная здесь перегрузка ==(lhs: Wrapped?, rhs: _OptionalNilComparisonType) -> Bool
. Вы можете увидеть его реализацию в строках 449-481 из Optional.swift
.
Его левый операнд равен Wrapped?
, он же Optional<Wrapped>
.
Его правый операнд _OptionalNilComparisonType
. Это специальный тип, который соответствует ExpressibleByNilLiteral
.
Необязательно, это один тип, который соответствует ExpressibleByNilLiteral
, что означает, что вы можете написать let x: Optional<Int> = nil
, и компилятор может использовать этот протокол, чтобы понять, что nil
должно означать Optional<Int>.none
. Протокол ExpressibleByNilLiteral
существует, чтобы позволить этому поведению быть расширяемым другими типами. Например, вы можете представить себе инфраструктуру взаимодействия Python, где вы хотели бы иметь возможность сказать let none: PyObject = nil
, которая может быть передана в Python
и преобразована в None
, Python в нулевой тип.
Здесь происходит то, что левая сторона (y
, типа Any
) повышается до типа Any?
. Повышенное значение имеет тип Optional<Any>.some(old_value_of_y)
. Правая часть, nil
, используется для создания экземпляра значения _OptionalNilComparisonType
, эквивалентного вызову _OptionalNilComparisonType.init(nilLiteral: ())
.
Таким образом, ваш сайт вызова эквивалентен:
Optional<Any>.some((Optional<Any>.none) as Any) == _OptionalNilComparisonType(nilLiteral: ()) /*
↑ ↑↖︎_the value of x_↗︎ ↑↑
↑ ↖︎_the value of y _________↗︎↑
↖︎_the value of y after promotion to optional__↗︎ */
В соответствии с реализацией, левая сторона - some
, правая сторона - nil
, и поэтому они неравны.
Вы можете сказать:
Ну, это довольно неочевидное поведение, это не то, что я хотел.
На что я бы ответил:
Не игнорируйте ошибки компилятора, они Вы правы:
предупреждение: сравнение необязательного значения типа Any
с nil
всегда возвращает false