Swift のコードでは String? や Int? といった、 末尾に ? が付いた型によく出会います。
これらは Optional(オプショナル)型と呼ばれ、 Swift の安全性を支える重要な仕組みの一つです。
一方で、 「なぜ Optional 型が必要なのか」 「なぜそのまま使えないのか」 といった点で、最初は戸惑うことも多いと思います(私もその一人です)。
本記事では、Swift の公式ドキュメントをもとに、
Optional 型の役割と考え方を整理します。
あわせて、Optional を安全に扱うための構文である
if let と guard let についても、
それぞれの役割に焦点を当てて解説します。
オプショナル型とは?
Swift の公式ドキュメントでは、Optional 型について次のように説明されています。
You use optionals in situations where a value may be absent.
意訳すると、「Optional 型は値が存在しない可能性がある場面で使用する」となります。
Optional 型は、単に 「nil を代入できる型」 という意味ではありません。
Optional 型は、「値が存在するかどうか」という状態を含めて表現する型です。
そのため、String と String? は 見た目は似ていても、まったく別の型として扱われます。
なぜ Optional 型が必要なのか
Optional 型が必要とされる理由を理解するために、 まずは Java のコードを例に見てみます。
String name = null;
System.out.println(name.length()); // 実行時例外(NullPointerException)が発生する
このコードはコンパイル時には問題ありませんが、
実行時に NullPointerException が発生します。
Java では、 「この値が null かもしれない」という情報が 型として表現されていないため、 実行するまで問題に気づけません。
同じことを Swift で書こうとすると、次のようになります。
let name: String? = nil
print(name.count) // コンパイルエラーSwift では、Optional 型の値をそのまま使おうとするとコンパイルエラーになります。
そのため、コードを書いている段階で問題に気づくことができます。
Optional 型を扱うための構文
アンラップ(unwrap)
Optional 型の値は、そのままでは中身を使うことができません。
これは、値が存在しない可能性(nil)を無視したまま処理が進んでしまうことを、
Swift がコンパイル時に防ぐようになっているからです。
そのため Swift では、
Optional 型の値を実際に使う前に、
「中身が存在することを確認し、取り出す」必要があります。
この「Optional の中身を取り出す操作」のことを、
アンラップ(unwrap) と呼びます。
Swift では、このアンラップを安全に行うための構文として、
if let や guard let が用意されています。
公式ドキュメントでは、特に if let , guard let のアンラップを安全に行う構文を Optional Binding と呼んでいます。
if let
if let は、
条件分岐の中で Optional の中身を使いたい場合に使用します。
func printLength(text: String?) {
if let unwrappedText = text {
// このブロック内では unwrappedText を String 型として使用可能
print(unwrappedText.count)
}
}この例では、text が nil でない場合のみ、
if ブロックの中で unwrappedText を 通常の String として扱えます。
guard let
guard let は、 値が存在することを「処理を続けるための前提条件」として扱いたい場合に使用します。
func printLength(text: String?) {
guard let unwrappedText = text else {
return
}
// ここ以降は unwrappedText を String 型として使用可能
print(unwrappedText.count)
}この例では、text が nil の場合はその時点で処理を終了し、
nil でない場合のみ、以降の処理が続行されます。
そのため、guard の後では unwrappedText を 通常の String として
関数の残り全体で使用できます。
実際の使用例(朝の実)
私が現在開発中の朝専用SNSアプリ「朝の実」では、CloudKit の CKRecord をアプリ内モデルに変換する処理において guard let を使用しています。
CKRecord は、「キーに対応する値が存在しない」「想定していた型と異なる」といったケースが起こり得ます。そのため、モデル生成の前提として 必須フィールドがすべて正しく取得できること を保証する必要があります。
そこで、各フィールドの取得に as?(失敗すると nil を返すキャスト)を使い、その結果を guard let でまとめて検証・アンラップしています。
guard の条件部に複数の let … = … as? … を並べることで、どれか1つでも失敗した時点で処理を早期に終了できます。
一方、guard を抜けた後は、取得した値が すべて非Optionalとして揃っている状態になるため、以降の初期化処理を安全に記述できます。
import Foundation
import CloudKit
extension Post {
init(record: CKRecord) throws {
// as? は「型が合わない / 値がない」場合に nil を返す
// guard let で必須項目をまとめてアンラップし、欠けていたら早期に失敗とする
guard
let id = record[PostRecordKey.id] as? String,
let authorId = record[PostRecordKey.authorId] as? String,
let text = record[PostRecordKey.text] as? String,
let createdAt = record[PostRecordKey.createdAt] as? Date,
let cheerCount = record[PostRecordKey.cheerCount] as? Int,
let isDeletedInt = record[PostRecordKey.isDeleted] as? Int,
let clientTimeZone = record[PostRecordKey.clientTimeZone] as? String
else {
throw CloudKitRepositoryError.decodingError
}
// guard let を抜けた後は、上の値はすべて「非Optional」として扱える
self.id = id
self.authorId = authorId
self.text = text
self.createdAt = createdAt
self.cheerCount = cheerCount
self.isDeleted = (isDeletedInt != 0)
self.clientTimeZone = clientTimeZone
}
}
まとめ
Optional 型は、 「値が存在しない可能性」を 型として明示するための仕組みです。
Swift では、 Optional 型をそのまま使うことはできず、 値が存在することを確認してから扱う必要があります。
if let と guard let は、
Optional 型を安全に扱うために用意された構文であり、
用途に応じて使い分けることが重要です。
