Why can't you assign an Optional to a variable of type `Any` without a warning?

  • A+
Category:Languages

The following code compiles without warning:

Version 1:

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:

Version 2:

var anything: Any let aDouble: Double? = 3 anything =  aDouble 

But this code does not throw a warning:

Version 3:

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.

 


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).

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: