Swift 5 has come
今回は当初予定ではSwiftとメモリ管理の話の続きを書く予定でしたが、3月25日にSwift 5がリリースされてしまったので急遽予定を変更し、Swift 5の話をすることにします。去年のWWDC 18 のWhat's new in Swift で“ early 2019” と予告されていたとおりですが(図1 ) 、筆者は今年は6月3日に開催されるWWDC19の寸前ぐらいだと憶測していたので少なからず驚いています。
図1 Swift 4から5へのロードマップ
Source Compatible w/Swift 4.x
では慌てる必要はあるかというと、実はほとんどありません。正しいSwift 4コードは正しいSwift 5コードでもあるからです。Swift 4とソースコード互換なのですから、既存のコードを直す必要はありません。この点において、Swift 5はSwiftの歴史の中で最も「大人しい」メジャーアップデートと言えるでしょう。
ABI frozen
ところが「節目」という意味では、これまでで最も大きなアップデートでもあるのです。なぜかというと、ABI(Application Binary Interface)が凍結されるからです。これがなぜ重要か。コンパイル済みのライブラリやモジュールを配布できるようになるからです。今まではソースコードごと配ってユーザにビルドしてもらう必要があったのがリンクするだけで使えるようになるというのは圧倒的な省資源省労力で、Cの完全上位互換だったはずのC++がCを置き換えられなかった最大の理由でもあります。
またこれにより、Swift 5で書かれたモジュールをSwift 6以降で使うことも可能になります。つまり、今まではObjective-Cで書かれていたFoundationなどの基幹モジュールもSwiftで書き直すことが容易になるということでもあり、実際そのようになっていくことと思われます。
#"Raw String"# at last!
Swift 5は前述のとおりSwift 4互換ですが、Swift 4ではありません。では何が違うのかというと、一番違うのはこれでしょう。筆者個人はこれだけでSwiftを5にする理由があるという立場です。
今までSwiftの文字列リテラル中のバックスラッシュ\ は必ず文字エスケープとして解釈され、\ そのものが多く出現する正規表現などを表記する際には\ \ を重ね書きする「お箸症候群」( chopstick syndrome)が不可避で、その一方でシングルクォート記号''
がずっと未使用で、Perlのq()
やRubyの%q()
に相当する、バックスラッシュをエスケープしない「raw string」がいつ実装されるのかと心待ちにしていたのですが、Swiftはここでも最高の後出しジャンケンぶりを[SE-0200] で発揮してくれました。
let s0 = "\ \ n\ n"
let s1 = #"\ n\ #n"#
s0 == s1 // true
おわかりいただけただろうか。つまり、#"
でquote"。#
でunquote。\ #
でescapeというしくみ。おもしろいのは#
を複数重ねて##"
でquote"。##
でunquote。\ ##
でescapeにもできること。Swift 4から導入済みの[SE-0168] の"""
も#"""…"""#
に対応しています。
let multiline = #"""
だっ…誰があんたの事なんか
いつもいつも変なこと言って
すごく嫌われてるの判んないの!?
きもち悪いわよ!!
"""#
驚くべきことに、5にいたってもまだ''
(singlequote)と`
(backquote)は未使用のまま。驚くべき記号の節約ぶりです。
Result<Success,Failure:Error>
Swiftの例外処理は、例外をthrow
する可能性のある函数をtry
して、例外がthrow
されたらcatch
するというものです。
import Foundation
do {
let u = URL(string:"https://example.com")!
let s = try String(contentsOf: u)
print(s)
} catch {
print(error)
}
try?
もあるので、catch
不要の場合は次のようにもできます。
let u = URL(string:"https://example.com")!
if let s = try? String(contentsOf: u) {
print(s)
}
しかし、例外をその場で処理するのではなく、成功失敗にかかわらず結果として扱いたい場合はどうすれば良いでしょう? [SE-0235] で追加され、Swift 5から標準装備となったResult
型がその期待に応えてくれます。
let u = URL(string:"https://example.com")!
let r = Result { try String(contentsOf: u) }
Result
はenum
なので、次のようにして結果を取り出すことができます。
switch(r) {
case let .success(value):
value
case let .failure(error):
error
}
また.get()
することで、do {} catch {}
しなおすこともできます。
do {
let s = try r.get()
print(s)
} catch {
print(error)
}
ExpressibleByStringInterpolation
Swiftの文字列展開(String Interpolation)では"\ (expression)"
のexpression
が文字化されて展開されます。これまではexpression
がCustomStringConvertible
プロトコルに準拠している場合は.description
プロパティの値が、そうでない場合はSwiftが自動生成した値がそれぞれ使われていましたが、[SE-0228] により独自の文字列展開を定義できるようになりました(リスト1 ) 。
リスト1 文字列展開サンプル
struct Point2D<T:Numeric> {
var x:T
var y:T
}
extension String.StringInterpolation {
mutating func appendInterpolation<T:Numeric>(_ value: Point2D<T>) {
appendInterpolation("(\ (value.x), \ (value.y))")
}
}
let p = Point2D(x:3.0, y:4.0)
print(p) // "Point2D(x:3.0, y:4.0)"
print("\ (p)") // (3.0, 4.0)
lessStringattached
Swift Blogの3月20日付記事 にもあるとおり、String
の内部実装がASCIIとUTF-16を切り替える方式から一元的にUTF-8
を用いる方式に変わりました(図2 ) 。
図2 Stringの内部実装の変化
これにより、たとえば.withCString
メソッドはわざわざCの文字列を生成する必要がなくなり、内部ポインタを返すだけでよくなったことで大幅なパフォーマンス向上を達成しました。
isMutiple(of:)
[SE-0225] が一部採用されたことにより、整数にisMultiple(of:)
メソッドが追加されました。実装としては、
extension BinaryInteger { // 名前はわざと変えてある
func isDivisible(by: Self)->Bool {
return self % by == 0
}
}
程度の簡単なものですが、英語的にはよりわかりやすいものとなっています。なお、もともとの提案にはisEven
およびisOdd
もありましたが、現状ではisMultiple(of:)
だけ定義されています。
令和はまだ未対応
リリースが3月25日だということからもわかるとおり、令和には未対応です(リスト2 ) 。
リスト2 令和はまだ未対応
"\ u{337E}".decomposedStringWithCompatibilityMapping // "明治"
"\ u{337D}".decomposedStringWithCompatibilityMapping // "大正"
"\ u{337C}".decomposedStringWithCompatibilityMapping // "昭和"
"\ u{337B}".decomposedStringWithCompatibilityMapping // "平成"
"\ u{32FF}".decomposedStringWithCompatibilityMapping // まだ"令和"じゃない!
以上駆け足でSwift 5を眺めていきましたが、ABIが凍結された一方、#""#
のような後出しじゃんけんっぷりは健在で、まだまだevolutionは続けられそうです。次回は前回 の続きから再開する予定です。
第1特集
MySQL アプリ開発者の必修5科目
不意なトラブルに困らないためのRDB基礎知識
第2特集
「知りたい」「使いたい」「発信したい」をかなえる
OSSソースコードリーディングのススメ
特別企画
企業のシステムを支えるOSとエコシステムの全貌
[特別企画]Red Hat Enterprise Linux 9最新ガイド
短期連載
今さら聞けないSSH
[前編]リモートログインとコマンドの実行
短期連載
MySQLで学ぶ文字コード
[最終回]文字コードのハマりどころTips集
短期連載
新生「Ansible」徹底解説
[4]Playbookの実行環境(基礎編)