try?とtry!の違い
今回は前回まとめたエラーハンドリングの内容の続きで、try?とtry!の違いについて整理します。
どちらも「エラーを投げる関数を呼ぶ」ための構文ですが、失敗時の挙動が真逆になります。
try?とは
try?はエラーが発生した場合にnilを返し、成功した場合はOptionalでラップされた値を返します。
try?を使用するとdo-catch構文を省略できますがエラーの詳細は分からず、失敗 or 成功のみの確認を行います。
- エラーをキャッチせずに、結果を Optional に変換します
- 成功したら値が入る、失敗したら nil
- つまり
do-catchを書かずに「結果だけ欲しい」時に使う省略形
try?の特徴
- エラー発生時はnilを返す
- 成功時はOptional<戻り値の型>を返す
- エラーの詳細情報は取得できない
- 安全でクラッシュしない
サンプルコード
enum FileError: Error {
case notFound
}
func readFile(named name: String) throws -> String {
if name == "validData.txt" {
return "ファイル内容:Hello, world!"
} else {
throw FileError.notFound
}
}
let result1 = try? readFile(named: "validData.txt") // 成功 → String?
let result2 = try? readFile(named: "invalidData.txt") // 失敗 → nil
print(result1) // Optional("ファイル内容:Hello, world!")
print(result2) // nil(クラッシュしない)
try!とは
try!はエラーが発生しないこと、絶対に失敗しないことの確証がある時だけ使用します。
エラーが発生した場合アプリは即座にクラッシュしてしまう為、使用される場面は限られてしまいます。
-
do-catchを省略できますが、エラーが起きた瞬間にクラッシュします - 「絶対成功する」と確証がある時だけ使う
try!の特徴
- エラー発生時はエラーでクラッシュ
- 成功時はアンラップされた値を直接返す
- エラーが絶対に発生しない場合のみ使用すべき
- コードは簡潔になるが危険
サンプルコード
enum FileError: Error {
case notFound
}
func readFile(named name: String) throws -> String {
if name == "validData.txt" {
return "ファイル内容:Hello, world!"
} else {
throw FileError.notFound
}
}
let text = try! readFile(named: "validData.txt") // 成功すればOK
print(text)
// 下はクラッシュします(実行時エラー)
// let crashText = try! readFile(named: "invalidData.txt")
try?とtry!の比較
| 項目 | try? | try! |
|---|---|---|
| 戻り値の型 | Optional<T> |
T |
| エラー発生時 |
nilを返す |
クラッシュ |
| 安全性 | 安全 | 危険 |
| 使用場面 | エラーを許容できる | エラーが絶対に発生しない |
まとめ
似たような構文ですが真逆の戻り値が返却されてしまいます。
使い分けをしっかり理解し、正しく扱う必要があると感じます。