Swift Digest
Blog | Swift.org Blog

Swift 6 の発表

Announcing Swift 6

このダイジェストはClaude Opus 4.7 / 4.8によって生成されたものです(License)。原文はこちら

この記事の要点

主な変更点

言語と標準ライブラリ

並行処理(データ競合安全性)

Swift はこれまでも、変数が使用前に初期化されていること、解放後のメモリにアクセスしないこと、配列の添字が範囲外でないことを保証するメモリ安全性を提供してきました。Swift 6 では、これらの安全性保証を並行コードにも拡張し、潜在的なデータ競合をコンパイルエラーとして診断する、オプトインの新しい言語モードが加わりました。

データ競合安全性のチェックは、Swift 5.10 では -strict-concurrency=complete コンパイラフラグによる警告として利用できました。Swift 6 では、Sendable の推論の改善と、ミュータブルな状態をあるアクターから別のアクターへ transfer(転送)する操作に対する新しいコンパイラ解析(SE-0414)により、データ競合安全性に関する警告の偽陽性が減りました。Swift 6 language mode への移行方法は Swift.org/migration にまとまっています。

Swift 6 はデータ競合安全性を劇的に扱いやすくするための出発点であり、その使い勝手は引き続き活発に開発が進められています。

Swift 6 にはあわせて、アトミック操作や新しい mutex API などの低レベル並行処理向けの Synchronization ライブラリSE-0410 / SE-0433)も導入されました。

typed throws

Swift 6 では、関数が throw するエラーの型をシグネチャの一部として指定できるようになりました(SE-0413)。クライアントコードで throw されたエラーをそのまま転送するジェネリックなコードや、Embedded Swift のようにメモリ割り当てができないリソース制約の厳しい環境で役立ちます。

func parseRecord(from string: String) throws(ParseError) -> Record {
  // ...
}

parseRecord(from:) を呼ぶと、Record のインスタンスを返すか、ParseError 型のエラーを throw します。do..catch では error 変数の型が ParseError と推論されます。

do {
  let record = try parseRecord(from: myString)
} catch {
  // 'error' has type 'ParseError'
}

typed throws は、エラーを throw する関数と throw しない関数を統一的に扱います。エラー型を指定しない throwsthrows(any Error) と等価で、throw しない関数は throws(Never) と等価です。throws(Never) の関数の呼び出しはエラーを throw しないため、呼び出し側でのエラー処理は不要です。

typed throws は、rethrows よりも正確な形で、引数のエラー型をジェネリック関数に伝播させるのにも使えます。たとえば Sequence.map は、クロージャ引数が throw するエラー型をそのまま伝播でき、クロージャと同じ型のエラーしか throw しないことを表現できます。

extension Sequence {
  func map<T, E>(_ body: (Element) throws(E) -> T) throws(E) -> [T] {
    // ...
  }
}

ParseErrorthrow するクロージャを渡すと mapParseErrorthrow し、throw しないクロージャを渡すと ENever と推論されて mapthrow しません。

所有権(ownership)

Swift 5.9 では、唯一の所有権を持つリソースをモデル化し、コピーに伴う実行時オーバーヘッドを取り除いて性能を意識したコードを書くために、~Copyable 構文による non-copyable な型が導入されました。Swift 6 では、これらの型をジェネリクスシステムで扱えるようになり(SE-0427)、copyable な型と non-copyable な型の両方で動くジェネリックなコードを書けます。

protocol Drinkable: ~Copyable {
  consuming func use()
}

struct Coffee: Drinkable, ~Copyable { /* ... */ }
struct Water: Drinkable { /* ... */ }

func drink(item: consuming some Drinkable & ~Copyable) {
  item.use()
}

drink(item: Coffee())
drink(item: Water())

Drinkable プロトコルは、適合する型が Copyable であることを要求しません。そのため、non-copyable な Coffee も copyable な Water も、ジェネリックな drink 関数に渡せます。

switch 文を、enum のパターンマッチでコピーを発生させないように書けるようにもなりました(SE-0432)。これにより、non-copyable なペイロードを持つ enum でも switch を使えるほか、ArrayDictionary のような copy-on-write のコンテナに基づく copyable なペイロードでも性能上の利点が得られます。

non-copyable な型は標準ライブラリでもすでに使われています。たとえば Synchronization ライブラリの新しい Atomic 型は ~Copyable に基づいており、OptionalResult は non-copyable な型を包めるようになり、unsafe buffer pointer 系の型は non-copyable な要素を指せるようになりました(SE-0437)。C++ 相互運用でも、C++ の move-only 型を Swift に公開するために non-copyable な型を使います。

C++ 相互運用

Swift 5.9 では、既存のプロジェクトへ Swift をシームレスに導入するため、C++ との双方向の相互運用が導入されました。Swift 6 では、C++ の move-only 型、仮想メソッド、デフォルト引数、std::mapstd::optional を含むより多くの標準ライブラリ型へと、相互運用のサポートが拡張されました。

コピーコンストラクタを持たない C++ 型は、Swift 6 から ~Copyable の non-copyable な型としてアクセスできます。また、性能のためにコピーコンストラクタを持つ C++ 型を Swift 側で ~Copyable として公開したい場合には、その C++ 型に SWIFT_NONCOPYABLE アノテーションを付けられます。

SWIFT_SHARED_REFERENCESWIFT_IMMORTAL_REFERENCE を付けた型に対する C++ の仮想メソッド呼び出しもサポートされました。一部のパラメータにデフォルト引数値を持つ C++ の関数やメソッドを呼ぶとき、Swift はそのデフォルト値を尊重し、明示的に引数を渡す必要がなくなりました。

Embedded Swift

Swift 6 には、マイクロコントローラのプログラミングのような組み込みソフトウェア開発に適した言語サブセットおよびコンパイルモードである Embedded Swift のプレビューが含まれます。ツールチェインは ARM と RISC-V のベアメタルターゲットに対応します。

Embedded Swift は、ジェネリックの特殊化(generic specialization)に頼ることで、小さく自己完結したバイナリを生成します。ランタイムや型のメタデータに依存しないため、メモリ制約の厳しいプラットフォームや、ランタイム依存の限られた低レベル環境での利用に適しています。

Embedded Swift は引き続き実験的機能であり、将来の Swift リリースで安定サポートされるまで開発が続けられます。

128-bit 整数

Swift 6 では、符号付き・符号なしの 128-bit 整数型SE-0425)が追加され、低レベルの整数プリミティブが一通りそろいました。すべての Swift プラットフォームで利用でき、標準ライブラリの他の固定幅整数型と同じ API を提供します。

生産性向上

Swift 6 には多くの生産性向上機能が含まれます。シーケンスのうち述語を満たす要素数を簡潔に数える count(where:)SE-0220)、value parameter pack の要素に対して自然な for ループを書ける pack iterationSE-0408)、実装の詳細が公開 API に漏れないようにする import のアクセス制御SE-0409)、関数の実装を合成・拡張する @attached(body) マクロSE-0415)、デフォルト引数としての式マクロSE-0422)などです。

Swift 6 で実装された言語進化のProposalの一覧は Swift Evolution dashboard で確認できます。

デバッグ

@DebugDescription による LLDB サマリのカスタマイズ

Swift 6 では、LLDB で p コマンドを使ったときや、Xcode・VSCode の変数ビューでオブジェクトがどう表示されるかを、任意のコードを実行しない書式スキームで簡単にカスタマイズできる新しいデバッグ用マクロが加わりました(SE-0440)。

CustomDebugStringConvertible に適合する型は、オブジェクトを説明する文字列を返す debugDescription プロパティを提供します。LLDB の po コマンドはこの computed property を呼び出します。一方 p コマンドは、LLDB の type summary formatter を使って、stored property の値から直接オブジェクトを整形します。

@DebugDescription は標準ライブラリの新しいマクロで、自分の型に対する LLDB の type summary をコード中で直接指定できます。このマクロは debugDescription プロパティを処理し、stored property を使った単純な文字列補間を LLDB の type summary に変換します。これにより、p コマンドや Xcode・VSCode の変数表示でも独自の書式が使われるようになります。

@DebugDescription
struct Organization: CustomDebugStringConvertible {
  var id: String
  var name: String
  var manager: Person
  // ... and more

  var debugDescription: String {
    "#\(id) \(name) [\(manager.name)]"
  }
}

explicit module による起動性能の向上

Swift 6 では、explicit module build を使うときのデバッガの起動性能が大きく向上しました。ローカルでビルドしたコードをデバッグする際、LLDB はプロジェクトのビルド成果物から、明示的にビルドされた Swift・Clang モジュールを直接 import できるようになりました。これにより、暗黙的な Clang モジュール依存をソースから再コンパイルする必要がなくなります。再コンパイルは時間がかかるうえ、ヘッダの検索パスの問題に影響されやすいものでした。

ライブラリ

Foundation

Swift 6 では、Foundation の実装がすべてのプラットフォームで統一されました。モダンでポータブルな Swift 実装はプラットフォーム間の一貫性を高め、より堅牢で、オープンソースです。macOS と iOS は Swift 5.9 から Swift 実装の Foundation を使い始めており、Swift 6 ではこの改善が Linux と Windows にも届きます。

JSONDecoderURLCalendarFileManagerProcessInfo などのコア型は、完全に Swift で再実装されました。これらは macOS 15 / iOS 18 と実装を共有し、クロスプラットフォームでの一貫性・信頼性・性能を新たな水準に引き上げます。FormatStyleParseStrategyPredicateJSON5 といった近年の API もすべての Swift プラットフォームで利用できるようになりました。

バイナリサイズに特に敏感なアプリでは、国際化・ローカライズデータを省いた、より絞り込んだサブセットを提供する FoundationEssentials ライブラリを import できます。

Swift Testing

Swift 6 では、Swift のために一から設計された新しいテストライブラリ Swift Testing が導入されました。テストの記述と整理を容易にする表現力の高い API を備え、#expect などのマクロでテスト失敗時に詳細な出力を提供し、パラメータ化により異なる引数でテストを繰り返せます。

@Test("Continents mentioned in videos", arguments: [
  "A Beach",
  "By the Lake",
  "Camping in the Woods"
])
func mentionedContinents(videoName: String) async throws {
  let videoLibrary = try await VideoLibrary()
  let video = try #require(await videoLibrary.video(named: videoName))
  #expect(video.mentionedContinents.count <= 3)
}

Swift Testing はマクロを最大限に活用します。@Test / @Suite のアタッチドマクロでテスト関数とスイート型を宣言し、トレイトと呼ばれる引数で挙動をカスタマイズします。#expect / #require の式マクロは期待される挙動を検証し、式とその部分値の豊かな表現を捕捉して詳細な失敗メッセージを生成します。

Swift Testing は Swift 6 ツールチェインに直接含まれるため、パッケージ依存を宣言せずに import Testing できます。Swift 6 のパッケージマネージャは、XCTest(存在する場合)に加えて Swift Testing のテストも自動でビルド・実行します。Apple の全プラットフォーム、Linux、Windows に対応します。

プラットフォームサポート

Swift 6 では Linux と Windows のサポートが全面的に強化され、より多くの Linux ディストリビューションと Windows アーキテクチャに対応しました。

Swift 利用者への影響

関連 Proposal・リンク