If let it act strange, when the type is explicitly specified

Say we have:

let a:Int? = nil // block not executed - unwapping done, type is inferred if let unwrapped_a = a { println(unwrapped_a) } // block not executed - unwrapping done, type is specified if let unwrapped_a:Int = a { println(unwrapped_a) } // block gets executed - unwrapping not done, new local constant + assignment is done instead? if let not_unwrapped_a:Int? = a { println(not_unwrapped_a) } 

So I have to assume that Swift does an expansion in the first case, but an assignment in the second case?

Isn't that too syntax to create confusion? I mean, yes, the compiler warns you that you are using an optional type when working with not_unwrapped_a , but still.

Update:

So, after Airspeed Velocity's answer, I found another (but practically the same) strange case:

 if let not_unwrapped_a:Int???? = a { println(not_unwrapped_a) } 

a will be seamlessly wrapped in Int???? . So it will be type Int????? (five) - because a already optional. And then it will unfold once.

+6
source share
2 answers

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 // you can just pass a straight value: f(i) // Swift will turn this into this: f(Optional(i)) 

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

+5
source

The purpose of the optional binding is to check the optional parameter for nil, expand and assign the optional type, enclosed in the optional. Thus, in my opinion, the first case - the correct way to use it - the only variation I would use is the addition of optional casting (useful, for example, if the option contains AnyObject ).

I would not use the second case, preferring an optional listing:

 if let unwrapped_a = a as? Int { ... } 

if, as @drewag noted in the comments, the type is explicitly specified to avoid ambiguity when it is not clear.

In my opinion, the 3rd case should generate a compilation error. I do not see any use of optional binding to assign optional to another optional. An optional binding is not a general built-in purpose (for example, if let x = 5 {...} ), so conceptually this should not work if the left side is optional - it should be treated as if the right side was not optional ( for which compilation does not work).

+4
source

Source: https://habr.com/ru/post/979930/


All Articles