nicael
nicael

Reputation: 18995

What makes Swift think that [String] is Int?

I can write some short and valid example of code:

var array = ["one","two"];
var name = array[0];

All is ok. No errors. name now contains one.

Now suppose I write:

var array = ["one","two"];
var name = array["0"];

It will be a error, and its clear why: array is not a dictionary and I should access it contents by index.

But why does error say that "Type Int does not conform to protocol ExtendedGraphemeClusterLiteralConvertible?

Why Int, if it is [String] ?


By the way, if I produce an error in another way

var array = ["one":0,"two":1];
var name = array[0];

It is clear: "type DictionaryIndex<String, Int> does not conform to protocol IntegerLiteralConvertible"

Upvotes: 0

Views: 317

Answers (3)

rintaro
rintaro

Reputation: 51911

subscript of Array declared as:

struct Array<T> : MutableCollectionType, Sliceable {
    //... 
    subscript (index: Int) -> T

It expect Int as index. then if you do:

array["0"]

It's wrong, because "0" is not Int. But, in other words, if "0" can be Int, it will be OK.

Here, "0" is what in Swift? it's string, but actually it is ExtendedGraphemeClusterLiteral. Swift has 2 string literal types, ExtendedGraphemeClusterLiteral and StringLiteral.

  • "" empty → StringLiteral
  • "A" just one character → ExtendedGraphemeClusterLiteral
  • "AB" two or more characters → StringLiteral

Anyway, try this on Playground:

extension Int: ExtendedGraphemeClusterLiteralConvertible {
    typealias ExtendedGraphemeClusterLiteralType = String
    public init(unicodeScalarLiteral value: String) {
        self = value.toInt() ?? 0
    }
    public init(extendedGraphemeClusterLiteral value: String) {
        self = value.toInt() ?? 0
    }
}

var array = ["one","two"];
var name = array["0"]; // -> "one"

"Type 'Int' does not conform to protocol 'ExtendedGraphemeClusterLiteralConvertible'" means...

compiler: "If Int conforms to ExtendedGraphemeClusterLiteralConvertible, I can compile this!"

Upvotes: 1

Nate Cook
Nate Cook

Reputation: 93276

That error message is because you are supplying a string literal, not an instance of the String type, where the compiler is expecting an Int (since that's what Array's subscript method accepts). The way the compiler would convert a string literal to an instance of type Int is to use a method in the StringLiteralConvertible or ExtendedGraphemeClusterLiteralConvertible protocols, so it checks to see if the Int type conforms to one of those. Since Int doesn't conform, you get the error message you're seeing.

This explains Daniel T's additional information:

var array = ["one","two"]
array["0"]                // trying to convert string literal to Int

var foo: Int = "0"        // trying to convert string literal to Int

var index = "0"
array[index]              // trying to convert String instance to Int

Likewise, your final example shows the compiler attempting the same thing—trying to convert an integer literal to an instance of DictionaryIndex<String, Int>, because a Dictionary instance's subscript can be passed either an instance of that dictionary's Key type or a DictionaryIndex<Key, Value>.

Upvotes: 2

Daniel T.
Daniel T.

Reputation: 33967

Interestingly, if you do this:

var array = ["one","two"];
var index = "0"
var name = array[index];

The error will be 'String' is not convertible to 'Int'.

I think this happens because the system attempts to convert "0" into an Int which means doing a lot of protocol checks. Array's index type is convertible to Self.Index which is a ForwardIndexType... Well, things get weird.

Whereas Swift has strong rules against implicit variable type conversion. So if you use a variable as an index, it is more clear.

Note that var foo: Int = "0" produces the same error as your array["0"].

Upvotes: 0

Related Questions