Reputation: 101
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
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
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
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