Swift3 例外メモ(try、try!、try?、do-catch)
try?
の仕組みとか曖昧になってしまったのでメモ。
例外のスロー
- 例外をスローする関数は
throws
を宣言(書く位置は引数宣言の直後) - スローする例外は
Error
プロトコルを実装している必要がある
enum MyError: Error { case notFound case fail(String) } func fatalError() throws -> String { throw MyError.fail("fatal error.") }
呼び出し元
すべての例外の補足
do { try throws宣言された関数呼び出し } catch { ... }
といった構文で例外をキャッチthrows
宣言された関数を呼び出すときには、頭にtry
、try!
、try?
のいずれかをつけるcatch { ... }
またはcatch let error { ... }
ですべての例外をキャッチ(switch
のdefault
みたいな)enum
のようにエラーの種類から網羅性はチェックされないので、網羅するにはすべての例外を補足するcatch
が必須
func catchAllErrors() { do { try fatalError() // `throws`宣言された関数を呼ぶには`try`が必要 } catch MyError.notFound { // エラーの種類を記述 // ... } catch MyError.fail(let message) { // enumの関連値はパターンマッチで取得できる // ... } catch let error { // enumと違って網羅するにはこれが必須 // ... } }
部分的に例外を補足
- すべての例外を補足しない場合は、関数の宣言に
throws
を記述する必要がある
func catchSomeErrors() throws { // 例外を網羅できていないので`throws`宣言が必須 do { try fatalError() } catch MyError.notFound { // ... } }
例外を強制的に無視する(try!)
try!
を使うと例外を無視できる(do-catch
が不要)- ただし例外が発生したときにはクラッシュする(強制アンラップと同じ挙動)
func forceCall() { try! notError() // 例外を無視する(が、発生したときはクラッシュする) }
例外を安全的に無視する(try?)
try?
を使うと、try!
と同じように例外を安全に無視できるtry!
と違い、例外が発生した場合でも単純に無視される- 呼び出した関数に戻り値がある場合は、戻り値が
nil
となる(失敗した、というニュアンスか)
func safeCall() { try? fatalError() // 戻り値がない場合はセーフな呼び出し let s1 = try? fatalError() // => nil let s2 = try? someString() // => some string }
まとめ
個人的にこんな感じで考えたらよいのかなという指針。
- 発生した例外に興味がなければ、
try?
を使って例外を無視する - 発生した例外に興味がある場合は、
do { try xxx() } catch { ... }
を使って補足 - 入力値が完全に固定など、確実に例外が発生しないと断言できる場合は
try!
を例外的に使う(かも)
個人的にはEither
あるいはResult
のほうが好きなのだけれど、シンタックスシュガーがよく出来ているので場合によっては例外の使用を検討しても良いのかも。