The following code compiles without warning:
var anything: Any
anything = "woof"
Makes sense... Any is any type, value type or reference type.
However, if we create an optional variable like a Double?
, this code throws a warning:
var anything: Any
let aDouble: Double? = 3
anything = aDouble
But this code does not throw a warning:
enum AnEnum<T>: {
case first
case second (T)
}
var anEnum: AnEnum<String> = .first
anything = anEnum
You can rationalize that version 2 throws a warning because Any
is not an Optional
type, and Double?
is an Optional
type. Trying to assign an Optional to a non-optional type is sort of a type mismatch.
However, under the covers, an Optional
is an enum
with a .none
case, and with a .some
case, where the .some
case has an associated value. My version 3 uses an enum
, AnEnum
, that also has 2 cases, the second of which has an associated value. The AnEnum
type is nearly identical to the Swift native Optional
type.
Why is assigning an AnEnum value to anything
ok, but assigning an Optional value to anything
is not ok?
(I started to answer this question: Swift dictionary with mix types (optional and non-optional))
And then realized that I didn't really know the answer.
How to declare an Optional? You can simply represent a Data type as Optional by appending ! or ? to the Type . If an optional contains a value in it, it returns value as Optional<Value> , if not it returns nil .
Any data type can be optional in Swift: An integer might be 0, -1, 500, or any other range of numbers. An optional integer might be all the regular integer values, but also might be nil – it might not exist.
Optional Types Syntax and Usage in Swift Here's the syntax for an optional type: <data-item> <var-name>:<data-type>? The declaration is similar to declaring regular variables, except that you add a question mark (?) beside the data type which makes it an Optional type.
An Optional is a type on its own, actually one of Swift 4's new super-powered enums. It has two possible values, None and Some(T), where T is an associated value of the correct data type available in Swift 4.
Quite simply, it's because Any is like a Roach Motel for Optionals.
The Roach Motel is a cockroach trap whose motto is, "Roaches check in, but they don't check out." The same is true for Any and Optionals. You can put an Optional into an Any, but you can never get it out again.
To see what I mean, let's first put something else into an Any and get it out again:
let s : String = "howdy"
let any : Any = s
let s2 = any as! String
s2 // "howdy"
Now let's try that with an Optional:
let s : String? = "howdy"
let any : Any = s
let s2 = any as! String? // error
Ooops! You can't cast a nonOptional "down" to an Optional, so the original Optional is lost.
The thing wrapped by the Optional is not lost. You can still unwrap it:
let s : String? = "howdy"
let any : Any = s
let s2 = any as! String
s2 // "howdy"
But now s2
is a String, not an Optional. The Optional is gone for good. You can't get it out. You can't find out that what was put into the Any was an Optional. It's gone.
So that's why putting an Optional into an Any always elicits a warning. The compiler is saying: "You can do this, but do you really understand what you're doing?" And if you do, there are ways to silence the warning (and the error message tells you what they are).
Optional
is more than just an enum. It's also an implicit promotion of every type. Any
and Optional
combine in weird ways that can be surprising. For example, T?
is Any
. T??
is also Any
. Any
is Any
, Any?
is Any
. Any??
is also Any
.
This creates a number of headaches, and subtle up-casts and down-casts make it very unclear what the optionality of an Any
really is. For example.
var anything: Any = ""
let aDouble: Double? = 3
anything = aDouble as Any
let x = anything as? Double
What's x
here? Well, aDouble
is Optional<Double>
, not Double
, so as? Double
should return nil
. But it doesn't because magic. It returns Optional(3)
. Which is frankly kind of weird (try to get the same thing with a version of MyOptional as an enum), but it would be really bad the other way. Consider:
let data: [String: Any] = ["aDouble": aDouble]
let y = data["aDouble"] as? Double
y
is Double?
here, not Double??
because of optional coalescing. And thank goodness. But it's bizarre. And if you had generics involved with conformances based on Optional
, this could get really confusing (and does; so many questions come up about that....) Now replace Double
above with Any
and it goes Inception on you really fast.
So the compiler is warning you that mixing Any
with Optional
is a bad idea and can lead to weird type results, and you definitely should be calling it out anytime you do it. (And maybe you should stop using Any
because it is almost never the type you really mean anyway.)
To make it the most concrete example I can think of, consider:
var anything: Any = ""
let one: Int = 1
let maybeTwo: Int? = 2
anything = one
anything as? Int // fine
anything = maybeTwo
anything as? Int? // illegal (see below)
anything as? Int // fine (but why? that's not what we put in.)
You'd think this would be parallel, but it isn't. You can't just pull out the type you put in when you throw Optionals into the mix.
BTW, interesting addendum, you can't downcast from Any
to an Optional
type.
let x = anything as? Double? // Illegal
That's really interesting, because anything
is a Double?
:
(lldb) p anything
(Any) $R4 = {
payload_data_0 = 0x4008000000000000
payload_data_1 = 0x0000000000000000
payload_data_2 = 0x0000000000000000
instance_type = Double?
}
So how can I tell the difference between "anything
is Double?
but nil" vs "anything
is String
"? You'd think you could test that, but I don't think you can, because none of this works consistently. Instead it tries to be convenient. But you can't push it too hard or all the seams will show.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With