Swift 3のStringのCharacter Viewを、なぜIntでsubscript出来ないのか分からない

mono 
Swift・iOSコラム
6 min readSep 28, 2016

--

SwiftのStringについて勉強中です。大体分かってきたものの、ちょっとしっくり来ないところがあって、悩んでいます。

まずsというStringを定義します。

let s = “12345”

Stringはコレクション操作など出来ないが、CharacterViewは出来る

例えば、Stringに対してfor文を使うと、以下のエラーが出ます。

Type ‘String’ does not conform to protocol ‘Sequence’

for c in s {
print(c)
}

charactersプロパティ(CharacterView型)に対しては出来ます。

for c in s.characters {
print(c)
}
// → 1 2 3 4 5

まず、ここは問題無く理解出来ます。リファレンスのStringの項に以下のように記載されています。

Character View

A string’s characters property is a collection of extended grapheme clusters, which approximate human-readable characters. Many individual characters, such as “é”, “김”, and “🇮🇳”, can be made up of multiple Unicode code points. These code points are combined by Unicode’s boundary algorithms into extended grapheme clusters, represented by Swift’s Character type. Each element of the characters view is represented by a Character instance

ざっくり言うと、文字列表現は色々複雑で、Character Viewでは目で見た1文字を1文字として扱う、みたいな感じですかね。

count操作も出来ます。

print(s.characters.count)
// → 5

CharacterViewに対してsubscript操作しようとするとコンパイルエラー

それぞれ、以下のようなコンパイルエラーが出ます。

print(s.characters[0])

Cannot subscript a value of type ‘String.CharacterView’ with an index of type ‘Int’

print(s.characters[0..<3])

Cannot subscript a value of type ‘String.CharacterView’ with an index of type ‘CountableRange<Int>’

正しく動くコード

以下がコンパイルの通るコードで、意図通り動きます。

let start = s.startIndex
print(s.characters[start])
// → 1
let end = s.index(start, offsetBy: 3)
let sTo3 = s.characters[start..<end]
print(sTo3)
// → 123

さらに、CharacterViewではなく、Stringに対して直接操作も可能です。

let start = s.startIndex
print(s[start])
// → 1
let end = s.index(start, offsetBy: 3)
let sTo3 = s[start..<end]
print(sTo3)
// → 123

CharacterViewはコレクション操作も出来るしcountでも結果が取れるのだからIntで要素にアクセス(subscript操作)出来ても良いのでは?

let start = s.startIndex

これは0とみなせそうですし、

let end = s.index(start, offsetBy: 3)

これは3と具体的な数値が明示されている通りなので、Character Viewに対しては以下のようにアクセスするのと同じ意味とみなせるのでは?と思ってしまいました。

print(s.characters[0..<3])

文字列は色々な表現があるので、以下の書き方が出来ないのはしっくりきています。

print(s[0..<3])

Intでsubscript操作出来なくて不便なので、CharacterViewあたりにそうできるようなextensionメソッドを追加したくなってしまいます。ただ、標準APIがそうなってないことには理由があるはずで、安易にそういうのを増やすべきでも無さそうとも思います。

というわけで、主に以下の2点について悩んでいる、という話でした。

  • どうしてCharacterViewのsubscriptメソッドがIntではなくIndexを受け付けるようになっているのか
  • extensionメソッドなどで便利にする時は、どのような定義が良いか

ご存じの方は教えていただけると、とてもありがたいです🙇

--

--