Implicit type casting in Swift - swift

Playing with the sample code from Swift Language Guide: Extensions I've extedned struct Double like this
extension Double {
func someFunc() {
print("someFunc")
}
}
I was surprised that this statement
2.someFunc()
did not generate compile time error like: Value of type 'Int' has no member 'someFunc'. I've expected value of 2 to be implicitly casted to Int but Swift casted it to Double. Why is that ? How does Swift determine that value of 2 in this case is of type Double ?
Then I've tried calling someFunc() like that
let x = 2
x.someFunc()
Here I get the expected compile time error
Does'n this contradict statement in Swift Programming Language 3.0.1 : Language guide : The basics : Type Safety and Type Inference?
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.
EDIT
From the responses I've learned that it happens because Double conforms to ExpressibleByIntegerLiteral protocol. However Float struct also does conform to it and some other types as well. Below I have created my struct that also conforms to that protocol. In the end however Double is chosen at compile time. Why? How is the precedence of method from one extension determined over the method from other extension ?
struct someStruct: ExpressibleByIntegerLiteral{
var i:Int = 0
init(integerLiteral value: Int64){
i = Int(value)
}
}
extension someStruct {
func someFunc() {print("Somestruct someFunc") }
}
extension Double {
func someFunc() { print("Double someFunc") }
}
4.someFunc()
//prints: Double someFunc

Double is one of the types that conforms to the protocol ExpressibleByIntegerLiteral. Since 2 is an integer leteral, the compiler can check which of the types conforming to the protocol has a someFunc() and since only Double does, there is no abiguity in this context.

Double conforms to ExpressibleByIntegerLiteral. In your example, the compiler sees that of the all the types that conform to ExpressibleByIntegerLiteral, only Double contains someFunc(), so it knows to create a Double out of 2.
As you noticed in your second example, this behaviour doesn't define implicit casting between types. It only applies to literals.

Related

Swift: binary operator '<' cannot be applied to two 'Comparable' operands

I have a protocol defined that has within it a function that returns an associated type that the user can define in their protocol implementation. The only requirement is that the associated type conform to Comparable.
I have another class that uses elements that conform to this protocol. The issue I'm running into is that the compiler is complaining that I can't compare elements of the associated type, even though I've specifically marked them as Comparable in the protocol definition. Anyone see what I'm doing wrong here?
protocol MyElement {
associatedtype T : Comparable
func getValue() -> T
}
class MyNode {
init(elements:[any MyElement]) {
// Sort the elements
let sortedElements = elements.sorted(by: { ( a:any MyElement, b:any MyElement ) -> Bool in
let aT = a.getValue()
let bT = b.getValue()
return aT < bT
})
}
}
The compiler complains at the line aT < bT:
binary operator '<' cannot be applied to two 'Comparable' operands
The < operator applies to two values of a single type that conforms to Comparable — not to two things designated merely as Comparable. A protocol does not conform to itself.
The use of any with MyElement doesn't affect this basic fact about the Comparable-constrained generic placeholder T. Since MyElement is generic over T, we would have to know that the two different MyElement-conforming objects you propose to sort resolve their T to the same type as one another — and in the code you wrote, we don't know that, so the compiler balks.

Swift Protocol Optional conformance via Non-Optional

I have a protocol with an optional property.
Most of the types that conform to this protocol will have a matching optional property. However, one has a non-optional property of the same type and name.
protocol SomeProtocol {
var foo: Int? { get }
}
struct StructA: SomeProtocol {
let foo: Int?
}
struct StructB: SomeProtocol {
let foo: Int // Type 'StructB' does not conform to protocol 'SomeProtocol'
}
Pressing Xcode's "Fix - Do you want to add protocol stubs?" button adds the optional version of the property, but the structure now has invalid duplicated variable names:
struct StructB: SomeProtocol {
let foo: Int
var foo: Int? { return foo } // Invalid redeclaration of 'foo'
}
In the { get }-only case, I had assumed that this would "just work" due to the non-optional always satisfying the constraints of the optional, similar to how you can return a non-optional in a function with an optional return type. But apparently that is not the case.
This works the same for functions as well; a protocol's func bar() -> Int? is not satisfied by a conforming type declaring func bar() -> Int.
Is there any way around this issue? I would prefer not to rename the variables or add intermediate getters.
Has this situation been considered for Swift? What is the rational for not allowing a non-optional to satisfy an optional protocol variable?
If the protocol provides a default implementation that returns an optional:
protocol SomeProtocol {
var foo: Int? { get }
}
extension SomeProtocol {
var foo: Int? { return nil }
}
protocol-conforming types can then provide an overriding non-optional version of the variable/function:
struct StructB: SomeProtocol {
let foo: Int
}
I found this discussed on the Swift Evolution forum:
At the first glance I thought there is a rule that allows us to satisfy protocol requirements with non-optional types, but this resulted in an error. Only after further investigation I noticed that a default implementation must exist in order to 'kind of override' the requirement with a non-optional version.
https://forums.swift.org/t/how-does-this-rule-work-in-regard-of-ambiguity/19448
This Swift team also discusses allowing non-optional types to satisfy optional-value protocols:
Would it make any sense to allow protocol requirement satisfaction with non-optional types, like with failable init's? (Probably with some implicit optional promotion.)
Yep, totally! Except for the part where this changes the behavior of existing code, so we'd have to be very careful about it. This is considered part of [SR-522] Protocol funcs cannot have covariant returns
which is tracked on Stack Overflow here:
Why can't a get-only property requirement in a protocol be satisfied by a property which conforms?
The extension-with-default-implementation solution offered by pkamb is helpful in terms of getting the compiler to recognize conformance, but you need to be aware of the strange behavior this can produce.
An object of the newly conforming type will return a different value depending on what type you cast it to (this also includes passing as to a parameter whose type is the protocol):
let bar = StructB(foo: 7)
let baz: SomeProtocol = bar
bar.foo // evaluates to 7
baz.foo // evaluates to nil (surprise!)
As someone recently commented on a related Swift bug ticket: "This can be quite surprising and perhaps could be considered a bug of its own?"
It definitely tripped me up.

Why can't Swift find a Float extension method when called on an integer literal?

I have defined a method on Float called printme, and when I try to call it with an integer literal, Swift fails to find the method:
extension Float {
func printme() {
print("value: \(self)")
}
}
12.printme() // error: value of type 'Int' has no member 'printme'
If I use an explicit cast it works:
(12 as Float).printme() // prints "value: 12.0"
Why, if Float conforms to the IntegerLiteralConvertible protocol, does 12.printme() fail to find the method on Float? It works if the type
is Double, but fails for Int32, UInt, and other types. Why does it work for Double, but not for Float?
Note that the following does work:
func printit(f: Float) {
print("value: \(f)")
}
printit(10) // prints "value: 10.0"
So, it fails when the method is called on the integer literal but not when the integer literal is a parameter to a function.
In Xcode 6.4 it fails in a different way:
12.printme() // error: cannot invoke 'printme' with no arguments
When you don't have an explicit type, Swift assumes either Int or Double. From the Swift book:
For example, if you assign a literal value of 42 to a new constant without saying what type it is, Swift infers that you want the constant to be an Int, because you have initialized it with a number that looks like an integer ... Likewise, if you don’t specify a type for a floating-point literal, Swift infers that you want to create a Double.
Float is not on the inferred type list for literals. If you change your extension to Double, it works (Xcode 7.1):
extension Double {
func printme() {
print("I'm a Double")
}
}
12.printme()
12.0.printme()
For these kind of extensions I look for protocols all types I want to affect conform too, instead of counting on it that the compiler will play nice and convert Double to Float and the other way around.
IntegerLiteralConvertible works for all, but then you have no access to the numeric value. If you add a constraint to either IntegerType or FloatingPointType you have access to toIntMax() and self.advancedBy(0)
extension IntegerLiteralConvertible where Self : IntegerType {
func printMe() {
print(self.toIntMax())
}
}
extension IntegerLiteralConvertible where Self : FloatingPointType {
func printMe() {
print(self.advancedBy(0))
}
}
let float = Float(10.234234)
float.printMe()
let double = Double(234.234234)
double.printMe()
let int = Int(234234)
int.printMe()
let int16 = Int16(234)
int16.printMe()

Swift implicit type conversion between C typedefs

I need to use a third party C library whose source I cannot modify and which makes heavy use of implicit type casting and typedefs to set values for its structs. They are all ints underneath and this is the preferred way of interacting with this library. I've previously used it in Objective C code without issue so now I am mostly porting some of my old code, but it feels like I am constantly hitting a brick wall with Swift.
tl;dr: how can I assign a different typedef value to a C struct member in Swift while automatically handling the type conversions (all typedefs are ints underneath)?
For example, consider the following definitions:
struct library_struct {
int member;
};
typedef enum library_consts {
LIBRARY_DEFINED_VALUE = 0
} library_consts;
In C or Objective C it would be absolutely acceptable to perform the following:
library_struct a;
a.member = LIBRARY_DEFINED_VALUE
However, attempting to do the same thing in Swift
var a: library_struct = library_struct()
a.member = LIBRARY_DEFINED_VALUE
results in an error:
Cannot assign a value of type 'library_consts' to a value of type 'Int32'
I tried several approaches:
Cast using Int32(). This leads to a Cannot find an initializer for type 'Int32' that accepts and argument list of type (library_consts) error.
Use LIBRARY_DEFINED_VALUE.rawValue. This won't work, because rawValue will return an UInt32, so I'm going to get the following error: Cannot assign a value of type 'UInt32' to a value of type 'Int32'
The only alternative is to cast again the value returned by rawValue to an Int32 like this: Int32(LIBRARY_DEFINED_VALUE.rawValue)
This works, but it feels wrong to make a double cast and it doesn't solve more complicated situations such as assigning a value of a different type (but still an int underneath) to a struct member such as the following:
enum library_consts
{
LIB_FALSE=0,
LIB_TRUE=1
};
typedef int lib_bool_t;
typedef struct another_struct {
lib_bool_t aFlag;
}
var b: another_struct = another_struct()
a.aFlag = LIB_FALSE
This will error out with "Cannot assign a value of type 'library_consts' to a value of type 'lib_bool_t'"
I am afraid that there is no easier solution if you cannot change the
C interface. Using the "Generated Interface" view in Xcode 7 you can
see that
enum library_consts
{
LIB_FALSE=0,
LIB_TRUE=1
};
typedef int lib_bool_t;
are mapped to Swift as
struct library_consts : RawRepresentable {
init(_ rawValue: UInt32)
init(rawValue: UInt32)
var rawValue: UInt32
}
typealias lib_bool_t = Int32
(the C int type is Int32 in Swift).
Swift does no implicit type conversions, which means that you have
to convert the types explicitly. In the second case it would be
var b: another_struct = another_struct()
b.aFlag = lib_bool_t(LIB_FALSE.rawValue)

Defining explicit conversion for custom types in Swift

What is currently the best/preferred way to define explicit conversions in Swift? Of the top of my head I can think of two:
Creating custom initializers for the destination type via an extension, like this:
extension String {
init(_ myType: MyType) {
self = "Some Value"
}
}
This way, you could just use String(m) where m is of type MyType to convert m to a string.
Defining toType-Methods in the source type, like this:
class MyType {
func toString() -> String {
return "Some Value"
}
}
This is comparable to Swift's String.toInt(), which returns an Int?. But if this was the definitive way to go I would expect there to be protocols for the basic types for this, like an inversion of the already existing *LiteralConvertible protocols.
Sub-question: None of the two methods allow something like this to compile: let s: MyString = myTypeInstance (as String) (part in parentheses optional), but if I understand right, the as operator is only for downcasting within type hierarchies, is that correct?
The pattern used in swift is the initializer. So for instance when converting an Int to UInt, we have to write:
var i: Int = 10
var u: UInt = UInt(i)
I would stick with that pattern.
As for the subquestion, the documentation states that:
Type casting is a way to check the type of an instance, and/or to treat that instance as if it is a different superclass or subclass from somewhere else in its own class hierarchy.
and
You can also use type casting to check whether a type conforms to a protocol
so no, the as keyword can`t be used to transform a value of a certain type to another type.
That can be tested in a simple way:
var i: Int = 10
var u: UInt = i as UInt
That generates an error:
'Int' is not convertible to 'UInt'
More about Type Casting