この記事の要点
- Swift 5.9 が正式リリースされました。表現力の高い マクロ、可変個の型を扱える パラメータパック、低レベルコードの性能を制御する 所有権(ownership)機能 という、長年望まれてきた 3 つの言語機能が目玉です。
- C++ との 双方向の相互運用 が導入され、Swift と C++ を混在させたコードベースで、Swift から C++ の API を直接呼び出せるようになりました。
- そのほか、
if/switchの式としての利用、新しいpackageアクセスレベル、DiscardingTaskGroup、custom actor executor など、開発を快適にする多くの機能が加わりました。 - 開発体験面ではデバッグの高速化・信頼性向上やクラッシュ時のバックトレース表示、エコシステムでは Swift Package Manager・swift-syntax・Windows サポートが強化されました。
主な変更点
言語と標準ライブラリ
マクロ
マクロは、繰り返しの多いボイラープレートを減らし、表現力の高いライブラリを Swift パッケージとして配布できるようにする仕組みです。マクロには 2 つの呼び出し形式があります。
関数のように単独で展開する freestanding な #マクロ名 形式です。
let _: Font = #fontLiteral(name: "SF Mono", size: 14, weight: .regular)
宣言に付加する @マクロ名 属性形式です。
// stored property の格納をディクショナリへ移し、
// stored property を computed property に変換する
@DictionaryStorage
struct Point {
var x: Int = 1
var y: Int = 2
}
マクロは組み込みの言語機能のように働きますが、通常のコードと見分けがつくようになっています。実装は、SwiftSyntax ライブラリを使ってソースファイルに挿入するコードを生成する、ただの Swift 関数として書きます。
マクロにより、ライブラリの利用者は、使われる文脈に応じて適応する強力な機能を簡単に取り込めます。たとえば新しい Observation モジュール(SE-0395)は、Swift のクラスのプロパティが変化したときに他のコードへ自動で通知する仕組みをマクロで提供します。
マクロは macOS と Linux で利用でき、Windows サポートは近日対応予定です。
パラメータパック
パラメータパック(SE-0393)を使うと、任意個数の型に対して動作するジェネリックな型や関数を書けます。
たとえばパラメータパックがないと、任意個の Optional の値がすべて nil でないかを確認する all 関数を書くには、対応したい引数の個数ごとに別々のオーバーロードを並べる必要があり、人為的な上限が生じてしまいます。
func all<W1>(_ optional: W1?) -> W1?
func all<W1, W2>(_ optional1: W1?, optional2: W2?) -> (W1, W2)?
func all<W1, W2, W3>(_ optional1: W1?, optional2: W2?, optional3: W3?) -> (W1, W2, W3)?
パラメータパックを使えば、上限のない 1 つの関数として表現でき、任意個の引数を渡せます。
func all<each Wrapped>(_ optional: repeat (each Wrapped)?) -> (repeat each Wrapped)?
パラメータパックを使う API の呼び出し側は直感的で、追加の作業は必要ありません。
if let (int, double, string, bool) = all(optionalInt, optionalDouble, optionalString, optionalBool) {
print(int, double, string, bool)
}
else {
print("got a nil")
}
所有権(ownership)
所有権機能は、性能が重要なコードでメモリ管理の挙動を細かく調整するのに役立ちます。
consume演算子(SE-0366): 変数をデイニシャライズし、その内容をコピーせずに transfer する(移す)ことをコンパイラに伝えます。consuming/borrowingパラメータ修飾子(SE-0377): 引数を渡す際の不要なコピーや参照カウント操作を取り除くためのヒントを与えます。- noncopyable な struct と enum(SE-0390): クラスのように代入時に意味のあるコピーができない一方で、struct や enum のように、ある時点で 1 つの格納場所だけがインスタンスを所有するため参照カウントを必要としない型を定義できます。
これらは、今後のさらなる進化の基盤となる大きな機能です。Swift は、より充実した variadic generics と所有権機能を備えた将来に向けて開発を進めています。
その他の機能
Swift 5.9 には、暮らしを快適にする小さな改善も含まれます。
if と switch を式として使い、変数への代入や戻り値に利用できます(SE-0380)。
statusBar.text = if !hasConnection { "Disconnected" }
else if let error = lastError { error.localizedDescription }
else { "Ready" }
新しい package アクセスレベル(SE-0386)により、同じパッケージ内の他のモジュールからは API にアクセスできるが、パッケージ外のコードからは隠す、という公開範囲を指定できます。大きなモジュールを複数の小さなモジュールに分割しつつ、内部実装をパッケージの利用者に晒さずに済みます。
並行処理では、結果を生成しないタスクグループ向けの DiscardingTaskGroup(SE-0381)と、アクターが実行される環境を正確に制御する custom actor executor(SE-0392)が加わりました。
開発体験
クラッシュ時のバックトレース
Linux では、Swift ランタイムがプログラムのクラッシュや Swift ランタイムエラーを捕捉し、プログラムの出力にバックトレースを表示するようになりました。バックトレーサはプロセス外で動作し、async 関数にも対応します。
この機能は macOS でも利用できますが、デフォルトでは無効です。有効にするには SWIFT_BACKTRACE=enable=yes を設定し、プログラムを com.apple.security.get-task-allow entitlement で署名します。
デバッグ
LLDB と Swift コンパイラに、Swift のデバッグをより速く・確実にする機能が加わりました。
p/poコマンドが、単純な式の評価でコンパイラを経由しないことで、frame variable(v)コマンドと同じくらい速く局所変数やプロパティを表示するようになりました。- Swift の式からジェネリックの型パラメータを参照できるようになり、型パラメータが特定の具体型でインスタンス化されたときだけ発火する条件付きブレークポイントを設定できます。
- コンパイラが生成するデバッグ情報が局所変数のスコープをより正確に扱うようになり、未初期化メモリに基づく変数がデバッガに表示されにくくなりました。
エコシステム
C++ 相互運用
Swift 5.9 では、一定種類の API について C++・Objective-C++ との双方向の相互運用に対応しました。たとえば次のような C++ 関数があるとします。
// Clang module 'PromptResponder'
#pragma once
#include <vector>
std::vector<std::string> generatePromptResponse(std::string prompt);
これを Swift のコードから直接呼び出せます。
import PromptResponder
let codeLines = generatePromptResponse("Write Swift code that prints hello world")
.map(String.init)
.filter { !$0.isEmpty }
for line in codeLines {
print(line)
}
C++ 相互運用は現在も活発に進化しており、実世界での採用から得られるフィードバックをもとに、一部は将来のリリースで変更される可能性があります。有効化の方法や対応する言語サブセットについては、公式の C++ Interoperability ドキュメント を参照してください。
Swift Package Manager
Swift Package Manager にも多くの改善が入りました。
- 新しい
packageアクセス修飾子に対応し、同じパッケージ内の別ターゲット/モジュールのシンボルを、publicにせずにアクセスできるようになりました。 CompilerPluginSupportモジュールによりマクロのターゲットを定義でき、独自の Swift マクロをライブラリの API として作成・配布できます(SE-0394)。- 新しい
.embedInCodeリソースルールにより、リソースの内容をバイト配列として生成し、実行可能ファイルに埋め込めます。 allowNetworkConnections(scope:reason:)設定により、command plugin にネットワークアクセス権限を与えられます。- パッケージレジストリへの公開(SE-0391)や、署名付きパッケージに対応しました。
- Swift SDK バンドル形式に基づくクロスコンパイルに対応しました(実験的機能、SE-0387)。
swift-syntax
swift-syntax は Swift コードのパースに不可欠なツールで、新しいマクロ機能を支えています。構文ノード名がより一貫性のあるものになり、新しい SwiftParser のエラー回復が大きく改善され、より正確なエラーメッセージを得られるようになりました。また、構文木の変更された部分だけを再パースする incremental parsing に対応し、ドキュメントも大幅に拡充されました。
サーバ
custom actor executor をはじめとする Swift 5.9 の機能が、サーバ向けエコシステムにも取り込まれつつあります。サーバワークグループは 2023 年の年次アップデート を公開し、主要ライブラリでの並行処理の採用拡大などの計画を示しています。
Windows プラットフォーム
Windows サポートは安定性と開発体験が大きく向上しました。インストーラが Visual Studio のインストール前後どちらでも動作するようになり、複数ツールチェインの並行インストールに向けた初期作業も始まりました。Windows SDK / Visual C++ ツールを制御する新しいフラグの追加、WinSDK モジュールのカバレッジ拡大、C++ 相互運用に向けた vcruntime モジュールの再構成のほか、Structured Concurrency の安定性が大きく向上し、AsyncStream の反復などでのスタックオーバーフローが解消されました。LSP・SwiftPM のパス処理改善や LLDB の初期対応も進んでいます。
Swift 利用者への影響
- ライブラリの表現力が大きく広がります。 マクロにより、ボイラープレートの削減や
Observationのような高度な機能を、Swift パッケージとして配布・利用できます。 - 可変個の型を扱う API が自然に書けます。 パラメータパックにより、引数の個数ごとにオーバーロードを並べる必要がなくなります。
- 低レベルコードの性能を制御できます。
consume演算子・consuming/borrowing修飾子・noncopyable な型により、コピーや参照カウントの挙動を細かく調整できます。 - C++ コードベースと連携しやすくなります。 双方向の相互運用により、Swift から C++ の API を直接呼び出せます(ただし一部は今後変更される可能性があります)。
- 日々の記述が快適になります。
if/switchの式、packageアクセスレベル、DiscardingTaskGroup、custom actor executor などが利用できます。
関連 Proposal・リンク
- 前のリリースについては Swift 5.8 リリース を参照してください。
- 本記事で触れた Proposal ダイジェスト:
- Swift 5.9 に含まれる言語進化のProposal一覧: Swift Evolution dashboard
- Swift のダウンロード
- The Swift Programming Language