WWDC Quick Look 💓 By SwiftGGTeam
A Swift Tour: Explore Swift’s features and design

A Swift Tour: Explore Swift’s features and design

元の動画を見る

ハイライト

すべての開発者向け 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 または nil
  • if 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 を満たしやすい

重要ポイント

  1. 値セマンティクスでデータをモデル化:新しい型定義時はデフォルトで struct を使用。値型の代入は自動コピーで状態を自然に分離 — 並行シナリオでより安全。参照セマンティクス(共有可変状態、同一性、継承)が必要な場合のみ class または actor を使用。始め方:既存 class をレビューし、共有状態の必要がなければ struct に変換。

  2. 暗黙的エラー処理の代わりに guard + throws を使用:guard 文は関数入口で不正入力を遮断、throws はエラー伝播パスを型シグネチャで可視化。呼び出し側は try でマーク — コンパイラがエラー処理の漏れを防止。始め方:配列/辞書を直接変更するコードを mutating メソッドに変換し、guard 検証と throws マークを追加。

  3. 手動ロックの代わりに actor を使用:共有可変状態は並行環境でデータ競合を起こしやすい。actor はアクセスを自動直列化、コンパイラが Sendable 制約をチェック — 手動ロックより信頼性が高い。始め方:class + lock で共有状態を管理するコードを見つけ、actor に置き換え、コンパイラに並行安全性を検証させる。

関連セッション

コメント

GitHub Issues · utterances