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 = 123
Using as
, like let i = 123 as Int8
Returning 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.