ハイライト
すべての開発者向け Swift 言語ツアー — 単なる構文機能の羅列ではありません。Swift コンパイラチームの Allan Shortlidge が、ソーシャルネットワークのグラフデータモデルを構築しながら、Swift の核心設計原則が実際のコードでどう機能するかを示します。
主要内容
Swift を書くうえで最もよくある混乱:struct と class はいつ使い分ける?Optional と throw はどう選ぶ?並行安全性はどう実現する?答えはドキュメント各所に散在し、初心者は体系的な理解を形成しにくい。
このセッションのアプローチは直接的 — 1 つのソーシャルネットワークプロジェクトが全体を貫通。「User をどう表現するか」から始まり、完全なグラフデータモデル、HTTP サーバー、コマンドラインクライアントを段階的に構築。言語機能の導入はすべて実際のニーズに対応 — 値セマンティクスでデータ分離、guard 文で不正入力を遮断、actor で共有状態を保護。
Swift の設計哲学はこの物語線上で明確に — 値型をデフォルト選択、コンパイラがエラーと nil の処理を強制、型システムがコンパイル時にデータ競合を排除 — これらの原則は相互に支え合い、一体として機能。
詳細
値セマンティクスと不変性
Swift は var で可変変数、let で不変定数を宣言。struct は値型 — 代入時に自動コピー:
struct User {
let username: String
var isVisible: Bool = true
var friends: [String] = []
}
var alice = User(username: "alice")
alice.friends = ["charlie"]
var bruno = User(username: "bruno")
bruno.friends = alice.friends
alice.friends.append("dash")
bruno.friends // ["charlie"], alice の変更の影響を受けない
キーポイント:
let usernameは作成後変更不可、var friendsは変更可能bruno.friends = alice.friendsは配列全体をコピー — 2 つの変数は独立- 値型で構成された struct は自動的に値セマンティクスを獲得 — Swift 並行安全性の基盤(03:04)
エラー処理と Optional
Swift は 3 種類の例外を区別:回復可能なエラーは throw、値が存在しない可能性は Optional、プログラムロジックエラーは precondition/fatalError。
struct User {
let username: String
var isVisible: Bool = true
public private(set) var friends: [String] = []
mutating func addFriend(username: String) throws {
guard username != self.username else {
throw SocialError.befriendingSelf
}
guard !friends.contains(username) else {
throw SocialError.duplicateFriend(username: username)
}
friends.append(username)
}
}
enum SocialError: Error {
case befriendingSelf
case duplicateFriend(username: String)
}
キーポイント:
mutatingは struct プロパティを変更するメソッドをマーク(03:05)guard文はエラー条件を検出 — 満たされない場合は関数を終了throwsはエラーを throw する可能性をマーク、呼び出し側はtryで明示的にマークSocialError.duplicateFriend(username: username)の関連値でエラーにコンテキストを付与 — 呼び出し側は「どのユーザーが重複したか」を把握private(set)で friends は読み取り公開、書き込み非公開 — 外部からはaddFriend経由でのみ変更
Optional は「値が存在しない可能性」の場面 — Dictionary の subscript アクセスは自然に Optional を返す:
func findUser(_ username: String) -> User? {
allUsers[username]
}
if let charlie = findUser("charlie") {
print("Found \(charlie)")
} else {
print("charlie not found")
}
キーポイント:
User?は Optional 型 —Userまたはnilif letで安全にアンラップ — nil でない場合のみ分岐に入る- 強制アンラップ
!は nil でクラッシュ — 100% 確信がある場合のみ使用
プロトコルとジェネリクス
プロトコルは継承階層に依存しない抽象化、ジェネリクスは制約を満たす任意の型にアルゴリズムを適用:
extension UserStore {
public func friendsOfFriends(_ username: String) throws -> [String] {
let user = try user(for: username)
let excluded = Set(user.friends + [username])
return user.friends
.compactMap { lookUpUser($0) } // [String] -> [User]
.flatMap { $0.friends } // [User] -> [String]
.filter { !excluded.contains($0) } // drop excluded
.uniqued()
}
}
extension Collection where Element: Hashable {
func uniqued() -> [Element] {
let unique = Set(self)
return Array(unique)
}
}
キーポイント:
compactMapは nil をフィルタしてアンラップ —map+filterより簡潔(16:13)flatMapはネストした配列を平坦化uniqued()はwhere Element: Hashable制約のカスタム拡張 — ハッシュ可能な要素を持つ Collection にのみ適用- プロトコル + ジェネリクスでこのアルゴリズムは Array、Set、Dictionary Keys など条件を満たす任意のコレクション型に適用可能
並行処理:actor と Sendable
Swift 6 はコンパイル時にデータ競合安全性を検証。共有可変状態は actor で保護必須:
actor UserStore {
var allUsers: [String: User] = [:]
}
router.get("friendsOfFriends") { request, context -> [String] in
let username = try request.queryArgument(for: "username")
return try await UserStore.shared.friendsOfFriends(username)
}
キーポイント:
actorは参照型でアクセスを自動直列化 — 同一時刻に 1 つの Task のみ actor メソッドを実行(22:24)- actor 外部からメソッド呼び出しには
awaitが必要 — 呼び出し側は待機中にサスペンドされる可能性 Sendableプロトコルは並行ドメイン間で安全に渡せる型をマーク、値型(struct)は Sendable を満たしやすい
重要ポイント
-
値セマンティクスでデータをモデル化:新しい型定義時はデフォルトで struct を使用。値型の代入は自動コピーで状態を自然に分離 — 並行シナリオでより安全。参照セマンティクス(共有可変状態、同一性、継承)が必要な場合のみ class または actor を使用。始め方:既存 class をレビューし、共有状態の必要がなければ struct に変換。
-
暗黙的エラー処理の代わりに guard + throws を使用:guard 文は関数入口で不正入力を遮断、throws はエラー伝播パスを型シグネチャで可視化。呼び出し側は try でマーク — コンパイラがエラー処理の漏れを防止。始め方:配列/辞書を直接変更するコードを mutating メソッドに変換し、guard 検証と throws マークを追加。
-
手動ロックの代わりに actor を使用:共有可変状態は並行環境でデータ競合を起こしやすい。actor はアクセスを自動直列化、コンパイラが Sendable 制約をチェック — 手動ロックより信頼性が高い。始め方:class + lock で共有状態を管理するコードを見つけ、actor に置き換え、コンパイラに並行安全性を検証させる。
関連セッション
- Embrace Swift generics — ジェネリック制約とプロトコル設計の深掘り — 本セッションのプロトコルとジェネリクス部分の自然な延長
- Explore structured concurrency in Swift — Task、async/await、actor 並行モデルの体系的解説
- Demystify explicitly built modules — Swift モジュールコンパイル機構を含む Xcode 16 ビルドシステムの変化
- Expand on Swift macros — マクロによるコンパイル時コード生成 — プロパティラッパーと result builder 以外の言語拡張手段
コメント
GitHub Issues · utterances