Никто, похоже, на самом деле не объяснял сообщение об ошибке, поэтому я продолжу и сделаю это.
Начиная с простого Int
Предположим, у вас есть целое число i
, которое я представлю в виде диаграммы:
let i = 123
// ┏━━━━━━━━━━━━┓
// ┃ Name: i ┃
// ┃ Type: Int ┃
// ┠────────────┨
// ┃ Value: 123 ┃
// ┗━━━━━━━━━━━━┛
Any
Вы можете "упаковать" i
в значение типа Any
. Этот контейнер "скрывает" Int
ness i
.
- Это дает преимущество, позволяя вам обрабатывать его вместе с другими значениями, стертыми по типу, например
"s"
или true
.
- ... но у него есть и обратная сторона: на самом деле мало что можно сделать со значением типа
Any
. * * * * * * * * * * * * * Any
так широко, это действительно не описывает, что вы можете сделать со значением Вы не можете .lowercased()
это как String
, вы не можете toggle()
это, как Bool
, все, что вы действительно можете сделать, это передать его, и в конце концов низвергнуть его в другой тип, который может делать больше вещей.
Когда вы помещаете i
в Any
, это выглядит примерно так:
let any123 = i as Any
// ┏━━━━━━━━━━━━━━━━━━┓
// ┃ Name: any123 ┃
// ┃ Type: Any ┃
// ┠──────────────────┨
// ┃ Value: ┃
// ┃ ┏━━━━━━━━━━━━┓ ┃
// ┃ ┃ Name: i ┃ ┃
// ┃ ┃ Type: Int ┃ ┃
// ┃ ┠────────────┨ ┃
// ┃ ┃ Value: 123 ┃ ┃
// ┃ ┗━━━━━━━━━━━━┛ ┃
// ┗━━━━━━━━━━━━━━━━━━┛
Any
"непрозрачен" для системы типов, так как система типов не может определить, что находится за рамкой рамки Any
(поэтому я представляю ее сплошным контуром).
Optional<T>
Необязательно - это обобщенное перечисление с двумя падежами: some
(в котором хранится ассоциированное значение с именем wrapped
) и none
(что не так). Он универсален для любого типа T
, но для целей этого примера я расскажу только о случае, когда T
равен Int
, поэтому Optional<Int>
, a.k.a. Int?
.
nil
- сокращение от Optional<T>.none
для любого типа T
.
let some123: Int? = 123 // implicitly wraps the value into an optional
let none: Int? = nil
// ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓ ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓
// ╏ Name: some123 ╏ ╏ Name: none ╏
// ╏ Type: Int ╏ ╏ Type: Int? ╏
// ┠──────────────────┨ ┠───────────────┨
// ╏ Case: .some ╏ ╏ Case: .none ╏
// ╏ wrapped: ╏ ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛
// ╏ ┏━━━━━━━━━━━━┓ ╏
// ╏ ┃ Name: i ┃ ╏
// ╏ ┃ Type: Int ┃ ╏
// ╏ ┠────────────┨ ╏
// ╏ ┃ Value: 123 ┃ ╏
// ╏ ┗━━━━━━━━━━━━┛ ╏
// ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛
В отличие от Any
, опция «прозрачна» для системы типов (поэтому я представляю ее пунктирной линией). Вы всегда можете увидеть тип значения внутри.
Композиция необязательных
Опциональные могут составлять друг с другом, действуя как русские матрешки:
let someSome123: Int?? = 123 // Shorthand for Optional<Optional<Int>>.some(Optional<Int>.some(123))
let someOfNone: Int?? = .some(nil) // Shorthand for Optional<Optional<Int>>.some(Optional<Int>.none)
let none: Int?? = nil // Shorthand for Optional<Optional<Int>>.none
// ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓ ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓ ┏╍╍╍╍╍╍╍╍╍╍╍╍╍┓
// ╏ Name: someSome123 ╏ ╏ Name: someOfNone ╏ ╏ Name: none ╏
// ╏ Type: Int?? ╏ ╏ Type: Int?? ╏ ╏ Type: Int?? ╏
// ┠────────────────────────┨ ┠───────────────────┨ ┠─────────────┨
// ╏ Case: .some ╏ ╏ Case: .some ╏ ╏ Case: .none ╏
// ╏ wrapped: ╏ ╏ wrapped: ╏ ┗╍╍╍╍╍╍╍╍╍╍╍╍╍┛
// ╏ ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓ ╏ ╏ ┏╍╍╍╍╍╍╍╍╍╍╍╍╍┓ ╏
// ╏ ╏ Type: Int? ╏ ╏ ╏ ╏ Type: Int? ╏ ╏
// ╏ ┠──────────────────┨ ╏ ╏ ┠─────────────┨ ╏
// ╏ ╏ Case: .some ╏ ╏ ╏ ╏ Case: .none ╏ ╏
// ╏ ╏ wrapped: ╏ ╏ ╏ ┗╍╍╍╍╍╍╍╍╍╍╍╍╍┛ ╏
// ╏ ╏ ┏━━━━━━━━━━━━┓ ╏ ╏ ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛
// ╏ ╏ ┃ Type: Int ┃ ╏ ╏
// ╏ ╏ ┠────────────┨ ╏ ╏
// ╏ ╏ ┃ Value: 123 ┃ ╏ ╏
// ╏ ╏ ┗━━━━━━━━━━━━┛ ╏ ╏
// ╏ ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛ ╏
// ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛
Это происходит, например, при попытке добавить словарь, в котором хранятся значения T?
. Есть 3 возможных результата:
- Для ключа существует значение, отличное от
nil
. Это похоже на константу someSome123
выше
- Для ключа существует значение, но значение
nil
. Это как постоянная someOfNone
выше
- Не существует значения для ключа. Это как постоянная
none
выше
Многие языки позволяют всем типам хранить значение null
. Это более слабый дизайн, потому что он не может различить № 2 и № 3. «Означает ли это null
, что значение отсутствует или что оно есть, но само значение равно null
?» Взаимозаменяемость опциональных компонентов учитывает это различие. Есть много других контекстов, в которых он может возникнуть, например, доступ к значению first
для Array<Optional<T>>
.
Составление Any
и Optional
Так же, как дополнительные параметры могут содержать дополнительные параметры, дополнительные параметры могут содержать значения типа Any
, и наоборот. Вот несколько примеров, которые воссоздают вашу ошибку:
let any123: Any = 123
let optionalOptionalAny123: Any?? = any123
let optionalAny123: Any? = optionalOptionalAny // ⚠️ warning: expression implicitly coerced from 'Any??' to 'Any?'
// ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓ ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓
// ╏ Name: optionalOptionalAny123 ╏ ╏ Name: optionalAny123 ╏
// ╏ Type: Optional<Optional<Any>> ╏ ╏ Type: Optional<Any> ╏
// ┠───────────────────────────────┨ ┠──────────────────────────────────┨
// ╏ Case: .some ╏ ╏ Case: .some ╏
// ╏ wrapped: ╏ ╏ wrapped: ╏
// ╏ ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓ ╏ ╏ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ╏
// ╏ ╏ Type: Optional<Any> ╏ ╏ ╏ ┃ Type: Any ┃ ╏
// ╏ ┠───────────────────────┨ ╏ ╏ ┠────────────────────────────┨ ╏
// ╏ ╏ Case: .some ╏ ╏ ╏ ┃ Value: ┃ ╏
// ╏ ╏ wrapped: ╏ ╏ ╏ ┃ ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓ ┃ ╏
// ╏ ╏ ┏━━━━━━━━━━━━━━━━━┓ ╏ ╏ ╏ ┃ ╏ Name: some123 ╏ ┃ ╏
// ╏ ╏ ┃ Name: any123 ┃ ╏ ╏ ╏ ┃ ╏ Type: Optional<Int> ╏ ┃ ╏
// ╏ ╏ ┃ Type: Any ┃ ╏ ╏ ╏ ┃ ┠─────────────────────┨ ┃ ╏
// ╏ ╏ ┠─────────────────┨ ╏ ╏ ╏ ┃ ╏ Case: .some ╏ ┃ ╏
// ╏ ╏ ┃ Value: ┃ ╏ ╏ ╏ ┃ ╏ wrapped: ╏ ┃ ╏
// ╏ ╏ ┃ ┏━━━━━━━━━━━┓ ┃ ╏ ╏ ╏ ┃ ╏ ┏━━━━━━━━━━━┓ ╏ ┃ ╏
// ╏ ╏ ┃ ┃ i: Int ┃ ┃ ╏ ╏ ╏ ┃ ╏ ┃ i: Int ┃ ╏ ┃ ╏
// ╏ ╏ ┃ ┠───────────┨ ┃ ╏ ╏ ╏ ┃ ╏ ┠───────────┨ ╏ ┃ ╏
// ╏ ╏ ┃ ┃ Value: 123┃ ┃ ╏ ╏ ╏ ┃ ╏ ┃ Value: 123┃ ╏ ┃ ╏
// ╏ ╏ ┃ ┗━━━━━━━━━━━┛ ┃ ╏ ╏ ╏ ┃ ╏ ┗━━━━━━━━━━━┛ ╏ ┃ ╏
// ╏ ╏ ┗━━━━━━━━━━━━━━━━━┛ ╏ ╏ ╏ ┃ ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛ ┃ ╏
// ╏ ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛ ╏ ╏ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ╏
// ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛ ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛
Как видите, как optionalOptionalAny123
, так и optionalAny123
содержат два дополнительных слоя и один слой типа стирание (как предусмотрено Any
). Это предупреждение существует, чтобы сказать вам, что, принуждая Any??
к Any?
, вы скрываете второй слой опциональности за скрытой завесой Any
.
Чтобы обойти это предупреждение, вы можете выполнить одно из трех его предложений:
укажите значение по умолчанию, чтобы избежать этого предупреждения
Отогните один слой опциональности, развернув его содержимое или прибегнув к значению по умолчанию, если оно nil
. Это редко хорошая идея, потому что разумные значения по умолчанию редко существуют.
let optionalAny123: Any? = optionalOptionalAny ?? 0
принудительно разверните значение, чтобы избежать этого предупреждения
Отогните один слой опциональности, принудительно развернув его. Это редко хорошая идея, потому что она требует, чтобы вы были абсолютно уверены, что у вас нет nil
.
let optionalAny123: Any? = optionalOptionalAny!
явно приведен к 'Any?' с "как-нибудь?" заставить замолчать это предупреждение
Вы можете оставить значение как есть (с 2 уровнями необязательности), скрыв один из слоев необязательности за вуалью Any
.Добавление as Any?
на самом деле ничего не делает во время выполнения.Это синтаксическая соль, которая указывает на то, что вы намеренно хотели этого поведения, а не ошибочно позволяли ему ускользнуть.
let optionalAny123: Any? = optionalOptionalAny as Any?
В идеале вы должны попытаться сгладить необязательность, как только сможете.Если вложенность необязательного элемента имеет какое-то значение в вашей системе, действуйте в соответствии с этим как можно скорее, а вскоре после этого сглаживайте необязательное значение.