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?
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.
Related
I can store a value in a variable of type Any quite easily, but I can't figure out how to access it.
Just plain trying to assign a to i gives me this error message:error: cannot convert value of type 'Any' to specified type 'Int'
And trying to cast it gives me this error message:
error: protocol type 'Any' cannot conform to 'BinaryInteger' because only concrete types can conform to protocols
let a: Any = 1
//this doesn't work
let i: Int = a
//this doesn't work
let i: Int = Int(a)
It doesn't work because Int doesn't have an initializer that accepts type Any. To make it work you need to tell compiler that a is actually an Int. You do this like this:
let a: Any = 1
let i: Int = a as! Int
Edit:
If you are not sure about type of a, you should use optional casting. There are many approaches.
let i1: Int? = a as? Int // have Int? type
let i2: Int = a as? Int ?? 0 // if a is not Int, i2 will be defaulted to 0
guard let i3 = a as? Int else {
// what happens otherwise
}
You can access it. It's just a.
But you can't do much beyond that. Aside from a handful of actually universal functions (print, dump, etc.), there's really not much that you can do with an Any.
There's a gradient of generality and usefulness. On one extreme is Any. It's nearly useless. It doesn't require anything of its conforming types. But as a result, it's incredibly general. Literally all types conform to it.
On the other extreme is a concrete type like Int. If you have a parameter that expects an Int, only one type of value is allowed: Int. But this specificity buys you utility. You know that this value supports being added, multiplied, converted to string, etc.
The only way to do anything useful with Any is to down-cast it with as/as?/as! into a more restricting (less general) type.
How are they different? I get a bit confused because they seem to be similar concepts.
How does understanding them help with optimizing compilation time?
From Swift's own documentation:
Type Safety
Swift is a type-safe language. A type safe language encourages you to be clear about the types of values your code can work with. If part of your code expects a String, you can’t pass it an Int by mistake.
var welcomeMessage: String
welcomeMessage = 22 // this would create an error because you
//already specified that it's going to be a String
Type Inference
If you don’t specify the type of value you need, Swift uses type inference to work out the appropriate type. Type inference enables a compiler to deduce the type of a particular expression automatically when it compiles your code, simply by examining the values you provide.
var meaningOfLife = 42 // meaningOfLife is inferred to be of type Int
meaningOfLife = 55 // it Works, because 55 is an Int
Type Safety & Type Inference together
var meaningOfLife = 42 // 'Type inference' happened here, we didn't specify that this an Int, the compiler itself found out.
meaningOfLife = 55 // it Works, because 55 is an Int
meaningOfLife = "SomeString" // Because of 'Type Safety' ability you will get an
//error message: 'cannot assign value of type 'String' to type 'Int''
Tricky example for protocols with associated types:
Imagine the following protocol
protocol Identifiable {
associatedtype ID
var id: ID { get set }
}
You would adopt it like this:
struct Person: Identifiable {
typealias ID = String
var id: String
}
However you can also adopt it like this:
struct Website: Identifiable {
var id: URL
}
You can remove the typealias. The compiler will still infer the type.
For more see Generics - Associated Types
Thanks to Swift’s type inference, you don’t actually need to declare a
concrete Item of Int as part of the definition of IntStack. Because
IntStack conforms to all of the requirements of the Container
protocol, Swift can infer the appropriate Item to use, simply by
looking at the type of the append(_:) method’s item parameter and the
return type of the subscript. Indeed, if you delete the typealias Item
= Int line from the code above, everything still works, because it’s clear what type should be used for Item.
Type-safety and Generics
Suppose you have the following code:
struct Helper<T: Numeric> {
func adder(_ num1: T, _ num2: T) -> T {
return num1 + num2
}
var num: T
}
T can be anything that's numeric e.g. Int, Double, Int64, etc.
However as soon as you type let h = Helper(num: 10) the compiler will assume that T is an Int. It won't accept Double, Int64, for its adder func anymore. It will only accept Int.
This is again because of type-inference and type-safety.
type-inference: because it has to infer that that the generic is of type Int.
type-safety: because once the T is set to be of type Int, it will no longer accept Int64, Double...
As you can see in the screenshot the signature is now changed to only accept a parameter of type Int
Pro tip to optimize compiler performance:
The less type inference your code has to do the faster it compiles. Hence it's recommended to avoid collection literals. And the longer a collection gets, the slower its type inference becomes...
not bad
let names = ["John", "Ali", "Jane", " Taika"]
good
let names : [String] = ["John", "Ali", "Jane", " Taika"]
For more see this answer.
Also see Why is Swift compile time so slow?
The solution helped his compilation time go down from 10/15 seconds to a single second.
How are they different? I get a bit confused because they seem to be similar concepts.
How does understanding them help with optimizing compilation time?
From Swift's own documentation:
Type Safety
Swift is a type-safe language. A type safe language encourages you to be clear about the types of values your code can work with. If part of your code expects a String, you can’t pass it an Int by mistake.
var welcomeMessage: String
welcomeMessage = 22 // this would create an error because you
//already specified that it's going to be a String
Type Inference
If you don’t specify the type of value you need, Swift uses type inference to work out the appropriate type. Type inference enables a compiler to deduce the type of a particular expression automatically when it compiles your code, simply by examining the values you provide.
var meaningOfLife = 42 // meaningOfLife is inferred to be of type Int
meaningOfLife = 55 // it Works, because 55 is an Int
Type Safety & Type Inference together
var meaningOfLife = 42 // 'Type inference' happened here, we didn't specify that this an Int, the compiler itself found out.
meaningOfLife = 55 // it Works, because 55 is an Int
meaningOfLife = "SomeString" // Because of 'Type Safety' ability you will get an
//error message: 'cannot assign value of type 'String' to type 'Int''
Tricky example for protocols with associated types:
Imagine the following protocol
protocol Identifiable {
associatedtype ID
var id: ID { get set }
}
You would adopt it like this:
struct Person: Identifiable {
typealias ID = String
var id: String
}
However you can also adopt it like this:
struct Website: Identifiable {
var id: URL
}
You can remove the typealias. The compiler will still infer the type.
For more see Generics - Associated Types
Thanks to Swift’s type inference, you don’t actually need to declare a
concrete Item of Int as part of the definition of IntStack. Because
IntStack conforms to all of the requirements of the Container
protocol, Swift can infer the appropriate Item to use, simply by
looking at the type of the append(_:) method’s item parameter and the
return type of the subscript. Indeed, if you delete the typealias Item
= Int line from the code above, everything still works, because it’s clear what type should be used for Item.
Type-safety and Generics
Suppose you have the following code:
struct Helper<T: Numeric> {
func adder(_ num1: T, _ num2: T) -> T {
return num1 + num2
}
var num: T
}
T can be anything that's numeric e.g. Int, Double, Int64, etc.
However as soon as you type let h = Helper(num: 10) the compiler will assume that T is an Int. It won't accept Double, Int64, for its adder func anymore. It will only accept Int.
This is again because of type-inference and type-safety.
type-inference: because it has to infer that that the generic is of type Int.
type-safety: because once the T is set to be of type Int, it will no longer accept Int64, Double...
As you can see in the screenshot the signature is now changed to only accept a parameter of type Int
Pro tip to optimize compiler performance:
The less type inference your code has to do the faster it compiles. Hence it's recommended to avoid collection literals. And the longer a collection gets, the slower its type inference becomes...
not bad
let names = ["John", "Ali", "Jane", " Taika"]
good
let names : [String] = ["John", "Ali", "Jane", " Taika"]
For more see this answer.
Also see Why is Swift compile time so slow?
The solution helped his compilation time go down from 10/15 seconds to a single second.
I'm new to Swift and I am confused about the following:
In the lines Int(something) and var x :Int = something, what is the difference between Int() and :Int?
In fact var x = Int(something) and var x : Int = something is exactly the same.
Unlike in Objective-C where int is a scalar type Int in Swift ist a struct and
structs must be initialized.
The former syntax is an explicit call of the initializer, the latter an implicit call by assigning the value
From a pure language perspective, the correct way to assign a value to an integer (or other numerical types) variable is:
let num = Int(16)
or any of its variants.
However Swift implements some syntactic sugar to make that less verbose - thanks to which you can rewrite the above statement as:
let num = 16
which is equivalent to:
let num: Int = 16
(thanks to type inference)
This is possible because the Int type implements the IntegerLiteralConvertible protocol, and by doing that the compiler is able to translate an integer literal into an initializer invocation.
There are several other protocols like that, for string, array, float, etc.
If you want to know more, I recommend reading Swift Literal Convertibles at NSHipster.
If you are wondering if you can do that on your own classes/structs, the answer is yes - you just have to implement the protocol corresponding to the literal type you want to use.
In Swift, constants can expressed with let keyword like this
let MyConstant = 100
and explicitly defined with type name like below
let MyConstant: Int = 100
what are the benefit of using second method?
In case the compiler can't figure out the type of the rhs, for example,
let x: Double = 1
Sometimes, the type inference will infer types that is less abstract than what you want. If you want your identifier to be less abstract, you can explicitly define the type of your identifiers.
For example (assume that return type of obj.getString() is NSString:
let someObject: NSObject = obj.getString()
let someString = obj.getString()
On the second line, the constant someString will have type of NSString, whereas the on the first line it will be what you explicitly defined.
If the type definition is omitted, its type is inferred, which for integer literals are inferred as Int, so in this case both statements are exactly the same.
Specifying the type in this case doesn't increase readability and just adds unnecessary noise since the type is clearly an Integer, but in cases where it's not obvious what the type is, e.g:
let MyConstant : Int = createNumber()
Then there's an argument for explicitly specifying the type info for the additional clarity.
Imagine you want to set a floating point value for amount.
And if you define it this way :
let MyConstant = 100
your constant will be treated as INT instead of floating point one , So you have to implicity define type or provide a value that will help compiler infer the type of constant. e.g We have two options
here
Case A
let MyConstant = 100.0
Case B
let MyConstant:Double = 100
If you are writing code for others and want it to be more expressive and readable the second approach is much better,bcz it shows the intent of program not just for compiler, but also for the coder.