oneWayTicket
oneWayTicket

Reputation: 101

Any? incorect semantics

I was playing around with some code in swift and encountered one interesting case. Lets start with a little preamble: suppose you create some optional variables:

let a: String? = "abcd"; let b: Int? = 4
print(
    "Type of \(a) is \(type(of: a))" //Type of Optional("abcd") is Optional<String>
    "Type of \(b) is \(type(of: b))", //Type of Optional(4) is Optional<Int>
    separator: "\n"
)

Then you force unwrap so types of au and bu are not optional.

let au = a!; let bu = b!
print(
    "Type of \(au) is \(type(of: au))", //Type of abcd is String
    "Type of \(bu) is \(type(of: bu))", //Type of 4 is Int

    au + String(bu), //abcd4
    separator: "\n"
)

Seem reasonable, but things start to go weird, when you try to apply same code to Optional<Any>:

let a: Any? = "abcd"; let b: Any? = 4
let au = a!; let bu = b!
print(
    "Type of \(a) is \(type(of: a))", //Type of Optional("abcd") is Optional<Any>
    "Type of \(b) is \(type(of: b))", //Type of Optional(4) is Optional<Any>
    "Type of \(au) is \(type(of: au))", //Type of abcd is String
    "Type of \(bu) is \(type(of: bu))", //Type of 4 is Int
    //au + String(bu),
    separator: "\n"
)

But now if you try to to do same concatenation au + String(bu), swift will produce compilation error, even though these two variables are known to be of some concrete type, as reported by swift itself. The error is:

error: protocol type 'Any' cannot conform to 'LosslessStringConvertible' because only concrete types can conform to protocols

This certainly looks like a bug, isn't it. Please, share your opinion.

Upvotes: 2

Views: 78

Answers (3)

Rob Napier
Rob Napier

Reputation: 299605

As others have noted, type(of:) is the dynamic, runtime type of a value. The compiler relies only on the static, provable type of a value. In the code above, the only thing that the compiler can prove is that au will be of type Any. One of the many types that conform to Any is String, but the compiler doesn't know that in all possible code paths, the value really will be a String.

Since there is no func + (Any, String) overload, this can't compile.

Note that Any? is a bizarre and dangerous type. The problems with it aren't the cause of this example, but the way that it interacts with Optional promotion can lead to significant ambiguity and confusion. Every type in Swift can be implicitly promoted to an Optional of that type. And every type in Swift can be implicitly cast to Any. Combining those two facts means that Any can be implicitly promoted to Any?, or Any??, or Any???, etc., and yet all of those are also subtypes of Any. This can create all kinds of subtle headaches. You should strongly avoid using Any; it is very seldom the right tool. But you should even more careful of allowing Any? to show up in your code. It is an indication that something has probably gone wrong.

Upvotes: 3

Siju
Siju

Reputation: 510

type(of: T) Returns the dynamic type of a value.

You can use the type(of:) function to find the dynamic type of a value, particularly when the dynamic type is different from the static type. The static type of a value is the known, compile-time type of the value. The dynamic type of a value is the value's actual type at run-time, which can be a subtype of its concrete type.

This explanation is taken from comment above type(of: T) function. Do Cmd+click on type(of: T) in Xcode to read more

Upvotes: 1

Shreeram Bhat
Shreeram Bhat

Reputation: 3167

type(of: T) method gets runtime type of any variable. That is why you are seeing (type(of: au) as String. But Swift will not allow implicit type casting for safety reasons. That is the reason you can not add Int and Double without casting in Swift. You need to cast explicitly for your code to work.

Upvotes: 1

Related Questions