4

Here's my test code:

var myDict: [String: AnyObject] = ["k":"v"]

var a = myDict["k"]
var b = a as String

var c = myDict["k"] as String

Here's my Swift playground in Xcode6-beta6:

Playground

According to the rules of type inference, doesn't complaining about c logically contradict not-complaining about b?

Liron Shapira
  • 1,817
  • 1
  • 14
  • 17
  • Copy and paste the code into the question. If you want to show the playground as well fine. Personally I'm not going to type your code and deal with possible typos in order to provide an answer. – zaph Aug 24 '14 at 21:22
  • Oops, I didn't think about that. Done. – Liron Shapira Aug 24 '14 at 21:35

3 Answers3

2

I believe that this is a bug. Part of what is going on here is that String is not an object. If you change the first line to:

var myDict: [String: Any] = ["k":"v"]

then everything is fine. So, given that string is not an object, casting a variable of type AnyObject? to a String should definitely yield an error. And, since the compiler has already decided that a is of type AnyObject? it should complain about casting a to a String.

Note that if you change the last line to:

var c = myDict["k"] as NSString

the error goes away supporting the notion that the issue is that String is not an object. You get the same complaint if you put an Int as the value in the array and try to cast that to an Int.

Update:

So the plot thickens. If you don't import Foundation or import something that imports Foundation, then you get additional errors. Without Foundation:

enter image description here

So clearly some of this has to do with the dual nature of Strings as non-objects and NSStrings as objects and the ability to use Strings as NSStrings when Foundation is imported.

vacawama
  • 133,454
  • 26
  • 238
  • 261
1

This has to do with the fact that Dictionary has two subscript overloads:

subscript (key: Key) -> Value?
subscript (i: DictionaryIndex<Key, Value>) -> (Key, Value) { get }

The first is the familiar one where you pass a key and it gives you an optional of the value; and you can use to set the value on a key.

The second one is less common. I believe DictionaryIndex is a kind of iterator into the dictionary, and you can use it as a subscript to directly get the key-value pair at that iterator.

When the compiler can't find an overload that matches (in this case, the first one doesn't match because it returns an optional, which cannot be cast to non-optional String), it just picks one arbitrarily (well, it seems arbitrary to me anyway) to show in the error. In this place, it picks the second one, which you don't recognize. That's why the error seems weird to you.

newacct
  • 110,405
  • 27
  • 152
  • 217
  • 1
    I don't think it's arbitrary; in my experience, the compiler carefully selects the wrong one :) – sapi Aug 25 '14 at 05:35
0

This works.

var c = myDict["k"] as AnyObject! as String   // "v"

To answer your question, the reason Swift complains could be that you are trying to do these two conversions in one go. Remember, the statement var a = myDict["k"] contains an implicit conversion already. The implied conversion is AnyObject?, so the above would also work like this:

var c = myDict["k"] as AnyObject? as String   // "v"

Note that the above would lead to a run time error if the key "k" where not defined. You would allow this to return nil by casting to String?.

Mundi
  • 77,414
  • 17
  • 110
  • 132
  • Ok but surely the Swift language intends to have transitive type-downcasting semantics. So do you agree this is a compiler bug? – Liron Shapira Aug 24 '14 at 21:51
  • Surely Swift should have lot's of fixes to eliminate tons of casting. – zaph Aug 24 '14 at 22:03
  • If there is any chance that the value is not a `String` an `if let` is needed. `if let c = myDict["k"] as AnyObject! as? String` – zaph Aug 24 '14 at 22:04