Case 1 and case 2 are identical β they represent both the contents of a
for the new variable. The only difference is that you leave Swift to output the type unwrapped_a
in option 1, while you manually specify the type in option 2. The main reason you need to do option 2 is if the source value was ambiguous - for example, if it there was an overloaded function that could return several types.
Case 3 is interesting.
Whenever you have a value, Swift will always try to easily upgrade it to an optional value packing, if that helps match types and compile code. Fast automatic type updates are rare (usually it implicitly updates Int16
to Int32
, for example), but the values ββfor options are an exception.
This means that you can pass values ββwhere necessary, without having to wrap it:
func f(maybe: Int?) { ... } let i = 1
So, in your last example, you told Swift that you want not_unwrapped_a
be Int?
. But its part is let
, which requires that a
be deployed before it is assigned to it.
Introduced with this, the only way Swift can make it work is to implicitly wrap a
in another optional one, so this is what it does. Now this is an optional parameter containing an optional containing nil. This optional nil parameter is an optional parameter containing a value (optional containing nil). A deployment that gives you an optional containing zero. Nothing seems to have happened. But this happened - it was wrapped a second time, then deployed once.
You can see this in action if you compile your sample code using swiftc -dump-ast source.swift
. You will see the phrase inject_into_optional implicit type='Int??'
. Int??
is optional, containing optional.
Options containing optional options, obscure edge cases - they can happen easily. For example, if you ever followed ... over an array containing optional values, or used an index to get the value from a dictionary containing additional parameters, then additional option options were involved in this process.
Another way to think about it is if you think of if let x = y { }
as a view * as a function, if_let
, defined as follows:
func if_let<T>(optVal: T?, block: T->()) { if optVal != nil { block(optVal!) } }
Now imagine if you provided a block
that accepted Int?
, that is, T
will be Int?
. So T?
will Int??
. When did you pass regular Int?
in if_let
along with this block, Swift then implicitly updated it to Int??
to compile it. Is this essentially what happens with if let not_unwrapped_a:Int?
.
I agree, an implicit optional update can sometimes be unexpected (even more surprising is that Swift will update functions that return options, i.e. if a function accepts (Int)->Int?
It will update (Int)->Int
, to return an optional option). But, apparently, this feeling of potential confusion is worth being more convenient in this case.
* view only