環境
- Swift 3.2.0
- XCode: 8.3.1
Q. String配列からRangeで取り出した部分列の型は[String]ではない
Swift 3で、配列からRangeオブジェクトを使って部分列を取り出す場合、以下のようなコードで実現可能です。
let strs: [String] = [ "a", "b", "c", "d" ]
print(strs[0..<2]) // ["a", "b"]
次に取り出した部分列を変数へ代入することを考えます。
普段自分はSwiftを書くとき、型推論を使わずなるべく明示的な型指定をするクセがありました。
また部分列なので、型も同じString配列だろうと思って次のようなコードを書いたところ
let strs: [String] = [ "a", "b", "c" ]
let partOfStrs: [String] = strs[0..<2] // error: ambiguous subscript with base type '[String]' and index type 'CountableRange<Int>'
と怒られてしましました...
error: ambiguous subscript with base type '[String]' and index type 'CountableRange<Int>'
とは一体なんだ...
普段Rubyなどを書いていて String配列の部分列の型は当然 [String]
だろうという感覚だったので、なぜエラーになるのかわからずハマってしまいました
A. Swiftの配列はArray型、Rangeで取り出された部分配列はArraySlice型
エラーを見ると取り出した部分列が 「[String]ではない」
と言われています。
そこで念のため型を調べてみます。
let strs: [String] = [ "a", "b", "c", "d" ]\
print(String(describing: type(of: strs)))
let partOfStrs: = strs[0..<2]
print(String(describing: type(of: partOfStrs)))
結果
> Array<String>
> ArraySlice<String>
となり、 ArrayではなくArraySlice という型になっていました。
実はSwiftでは配列から部分列を取り出した場合、ArraySliceという型でオブジェクトが生成されます
このArraySliceは元のArrayとほぼ同じインターフェイスを持ちながら、実態としては元のArrayオブジェクトへのViewになっています。
Apple公式 を見ると
The ArraySlice type makes it fast and efficient for you to perform operations on sections of a larger array. Instead of copying over the elements of a slice to new storage, an ArraySlice instance presents a view onto the storage of a larger array. And because ArraySlice presents the same interface as Array, you can generally perform the same operations on a slice as you could on the original array.
訳: ArraySlice型は大きな配列の部分列に対する処理を高速で効率的にする。新しいストレージに要素をコピーする代わりに、ArraySliceインスタンスは元の大きな配列へのViewを表す。かつ、ArraySliceはArrayと同じインターフェイスを持っているので、大抵の場合において元のArrayにするのと同じような処理がスライスに対しても可能である。
とあります。
なので普段使っている分にはArrayとArraySliceの違いを気にする必要はないようです。
ただし先程のコードのように型推論を使わず、明示的に代入すると当然型が違うということで怒られていたわけですね
細かいことですが、他の言語から来るとハマって「」となるのでメモしておきます。