Swift Digest
SE-0130 | Swift Evolution

Replace repeating Character and UnicodeScalar forms of String.init

Proposal
SE-0130
Authors
Roman Levenstein
Review Manager
Chris Lattner
Status
Implemented (Swift 3.0)

01 何が問題だったのか

Swift 3.0 より前の String には、同じ値を count 回繰り返して文字列を作るイニシャライザが Character 版と UnicodeScalar 版の2種類用意されていました。

public init(repeating repeatedValue: Character, count: Int)
public init(repeating repeatedValue: UnicodeScalar, count: Int)

一見すると便利ですが、"0" のような文字列リテラルはどちらの型としても解釈できるため、次のコードはあいまいとしてコンパイルエラーになります。

let x = String(repeating: "0", count: 10)
// error: ambiguous use of 'init(repeating:count:)'

エラーを避けるには、呼び出し側で型を明示するキャストが必要でした。

let zeroes = String(repeating: "0" as Character, count: 10)
// or
let zeroes = String(repeating: "0" as UnicodeScalar, count: 10)

"0" を10個つなげた文字列が欲しい」というよくある用途に対して、型名を書き添えなければならないのは過剰な負担です。加えて、CharacterUnicodeScalar のどちらを選ぶかで挙動が変わるわけでもなく、ユーザーから見れば区別にほとんど意味がありません。

同じ不整合は String.append 側にも存在し、UnicodeScalar を引数に取るオーバーロードが String を取るオーバーロードと並存していました。

public mutating func append(_ x: UnicodeScalar)
public mutating func append(_ other: String)

こちらは UnicodeScalar 版がなくても String 版で完全に代替できるため、わざわざ別オーバーロードを用意する理由が乏しい状態でした。

02 どのように解決されるのか

繰り返し用のイニシャライザを String を受け取る単一のAPIに統合し、あいまい性の原因そのものを取り除きます。

新しいイニシャライザ

Character 版と UnicodeScalar 版の2つに代わり、次の1つだけが提供されます。

public init(repeating repeatedValue: String, count: Int)

繰り返す値が String になったため、これまで曖昧だった呼び出しもキャストなしでそのまま書けます。

let zeroes = String(repeating: "0", count: 10)
// "0000000000"

単一の文字だけでなく、任意の文字列を繰り返せるようになったのも実用上の利点です。

let separator = String(repeating: "-=", count: 5)
// "-=-=-=-=-="

append の整理

String.append からも UnicodeScalar を取るオーバーロードが削除され、String を取る次の1つに集約されます。

public mutating func append(_ other: String)

UnicodeScalar を直接追加していたコードは、String に変換して渡すか、String リテラルとして書き直せば同じ結果が得られます。

var s = "abc"
let scalar: UnicodeScalar = "!"
s.append(String(scalar))
// s == "abc!"

既存コードへの影響

削除されるAPIを使っていたコードは書き換えが必要です。典型的には次のような対応になります。

  • String(repeating: x as Character, count: n) / String(repeating: x as UnicodeScalar, count: n)String(repeating: String(x), count: n)、または繰り返したい値を直接 String リテラルとして渡す形に変更
  • s.append(scalar)scalarUnicodeScalar)→ s.append(String(scalar))

いずれも機械的に置換可能で、fix-itによる自動マイグレーションも想定されています。