'Where should I put my question mark in Swift?
I can see in the Apple documentation, when if let, the ? is used after as. But I tried this and the compile would not complain and the code behaved the same as as? NSError. So is this acceptable in Swift (not standard though), and if it is, is there any difference?
if let error = error as NSError? { ...
when it comes to
var arr = [Int8?]()
arr.append(29 as Int8?)
arr.append(1 as? Int8) // warning: Conditional downcast from literal to 'Int8' always fails; consider using 'as' coercion
Why in this case the downcasting will always fails?
Solution 1:[1]
For the purposes of most of this answer, I'll ignore the bridging features Swift has for inter-operating with Objective-C (when Foundation is imported).
re: arr.append(29 as Int8?)
as works just like a type annotation. No values are changed, you're just giving extra type information to the compiler, so arr.append(29 as Int8?) works as if you had written:
var arr = [Int8?]()
let i: Int8? = 29
arr.append(i)
However, the compiler already knows that arr is an [Int8?] (a.k.a. Array<Optional<Int8>>), whose Element type is Int8?. As a result, it already knows that the argument to append needs to be an Int8?. This, coupled with the fact that Swift can automatically promote a non-optional value (e.g. Int8) into an optional value (Int8?) when that's useful, you could just write:
arr.append(29)
re: arr.append(1 as? Int8)
This snippet needs a bit more explanation. You see, 1 is not an Int in Swift, although it can be.
It's an integer literal, which can be used to initialize a value of any type that conforms to ExpressibleByIntegerLiteral.
The fact that above you're able to write let i: Int8? = 29 instead of let i: Int8? = Int8(29) comes as a direct consequence; Int8 conforms to ExpressibleByIntegerLiteral (as does Float, Double, and every other signed and unsigned integer type).
The compiler will use contextual type information to pick what the best ExpressibleByIntegerLiteral-conforming type a given integer literal should be. This could come from several places:
An explicit type annotation, like
let i: Int8 = 123Using
as, likelet i = 123 as Int8Returning from a function with a known return type, such as:
func returnsInt8() -> Int8 { return 123 // We know this must be Int8 from the return type }Passing as an argument to a parameter with a known type, such as:
func takesAnInt8(_: Int8) {} takesAnInt8(123) // This argument can fit the parameter's type if it's Int8
In the absence of any of this type contextual information, the compiler will default to initializing literals into IntegerLiteralType, which is Int by default.
So when you write: 1 as? Int8, it's as if you wrote:
let i = 1 // (this is an `Int`)
arr.append(i as? Int8)
The problem becomes clear: i is statically typed to an Int, and there no scenario in which Int is an Int8, so this cast will always fail.
re: error as NSError?
This works because you have Foundation imported, which introduces some magical bridging that's intended to make interoperation with Objective C. For example, you can do:
let aSwiftString: Swift.String = "abc"
someObjCApi(aSwiftString as NSString) // Now it's a `Foundation.NSString`
Which will cause the runtime value to be bridged. As you saw, you can also bridge from Swift.Error to NSError (and from Swift.Error? to NSError?). This complicates the language a bit, because it means that the explanation of "as only does static type annotation with no runtime effect" is no longer true.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Alexander |
