基本的な構文
//エラーを列挙型で定義
enum SomeError: Error {
case error1
//列挙型のケースの連想値を利用することで、エラーに付随する情報を表現することもできます。
case error2(reason: String)
}
do {
//throw文によるエラーが発生する可能性のある処理
throw SomeError.error2(reason: "何かがおかしいようです")//Errorプロトコルに準拠した値
} catch SomeError.error1 {
//エラー処理
print("error1")
} catch SomeError.error2(let reason) {
//エラー処理
print("error2: \(reason)")
} catch { //パターンの指定がないcatch節はすべてのエラーにマッチ
//エラー処理
print("Unknown error: \(error)")
}
//実行結果
//error2: 何かがおかしいようです
Errorプロトコル
throw文のエラーを表現する型は、Errorプロトコルに準拠している必要があります。
Errorプロトコルは準拠した型がエラーを表現する型として扱えることを示すためのプロトコルで、準拠するために必要な実装はありません。
Errorプロトコルに準拠する型は、列挙型として定義することが一般的です。
do-catch文
throw文によるエラーが発生する可能性のある処理をdo節に記述し、catch節にエラー処理を記述します。
throw文はエラーを発生させる文で、Errorプロトコルに準拠した値を使って「throw エラー値」のように書きます。
do節の実行中にthrow文によるエラーが発生すると、catch節に実行が移ります。
catch節は複数続けて書くことができ、最初にマッチしたcatch節のブロックのみが実行されます
throwsキーワードを使用しての構文
関数、イニシャライザ、クロージャの定義にthrowsキーワードを追加すると、それらの処理の中でdo-catch文を用いずにthrow文によるエラーを発生させることができます。
enum OperationError : Error {
case overCapacity
}
//throwsキーワードを持つ関数
func sam(_ int: Int) throws -> Int {
guard int < 10 else {
//do-catch文を用いずにthrow文によるエラーを発生させる
throw OperationError.overCapacity
}
return int
}
//関数の呼び出し時にdo-catch文を使用
do {
try sam(11)
} catch OperationError.overCapacity {
print("数値が大き過ぎます")
}
//実行結果
//数値が大き過ぎます
※)定義にthrowsキーワードを指定していない場合、do-catch文で囲まれていないthrow文によるエラーはコンパイルエラーとなります。
tryキーワード
throwsキーワードが指定された処理(関数)を呼び出すには、それらの処理の呼び出しの前にtryキーワードを追加してtry 関数名(引数)のように記述します。tryキーワードを用いた処理の呼び出しは、throw文と同様に、do-catch文のdo節とthrowsキーワードが指定された処理の内部のみで使用できます。
イニシャライザに使用
throwsキーワードをイニシャライザに使用することで、インスタンス化の途中に発生したエラーを呼び出し元に伝えることができます。
下記はイニシャライザに渡す引数の値が13以上19以下でなければSamError.overCapacityを発生させ、インスタンス化できないようにしています。
enum SamError: Error {
case overCapacity(str:String)
}
struct Sam {
let a: Int
init(_ arg: Int) throws {
guard case 13...19 = arg else {
throw SamError.overCapacity(str: "数値が範囲外です")
}
self.a = arg
}
}
do {
try Sam(10)
} catch SamError.overCapacity(let str) {
print(str)
}
//実行結果
//数値が範囲外です
クロージャがエラーを発生させる場合
enum CutomErr: Error {
case err1(str:String)
}
func sam(a:Int, handler: (Int) throws -> Int){
do {
try handler(a)
} catch CutomErr.err1(let a) {
print(a)
}catch{
}
}
sam(a:2){b throws -> Int in
if b<10 {
throw CutomErr.err1(str: "値が小さいです。")
}
return b*10
}
//値が小さいです。
rethrowsキーワードを使用しての構文
関数やメソッドをrethrowsキーワードを指定して定義することで、引数のクロージャが発生させるエラーを関数の呼び出し元に伝播させることができます。
rethrowsキーワードを指定するには、関数やメソッドが少なくとも1つのエラーを発生させるクロージャを引数に取る必要があります。
enum SomeError: Error {
case err1(str:String)
}
//関数はrethrowsキーワードを用いて定義されているので、
//クロージャが発生させるエラーの処理を関数の呼び出し元に任せることができます。
func samFunction(_ closure: () throws -> Void) rethrows {
//エラーを発生させるクロージャを引数にとり実行
try closure()
}
do {
try samFunction{()->Void in
throw SomeError.err1(str:"SomeError")
}
} catch SomeError.err1(let str) {
// 引数のクロージャが発生させるエラーを、関数の呼び出し元で処理
print(str) // SomeError
}
関数内で引数のクロージャが発生させるエラーを処理
関数内で引数のクロージャが発生させるエラーを処理し、別のエラーを発生させることもできます。
enum SomeError: Error {
case err1
case err2
}
func samFunction(_ closure: () throws -> Void) rethrows {
do {
try closure() //エラー(SomeError.err2)が発生
} catch {
//上記でエラーが発生したので下記(エラーSomeError.err1が発生)が実行される
throw SomeError.err1
}
}
do {
try samFunction{()->Void in
throw SomeError.err2
}
//関数samFunctionを実行した結果エラー(SomeError.err1)が発生
} catch {
//発生したエラーはSomeError.err1になる
error //err1
}
※)ただし、引数のクロージャが発生させるエラー以外をもとにしたエラーを発生させることはできません。
try!キーワード
throwsキーワードが指定された処理(関数)であっても、特定の場面では絶対にエラーが発生しないとわかっていてるケースもあると思います。
その場合は、tryキーワードの代わりにtry!キーワードを使用することでエラーを無視できます。
tryキーワードとは異なり、try!キーワードはdo-catch文のdo節やthrowsキーワードが指定された処理の内部でなくても使用できます。
enum SamError : Error {
case overCapacity
}
//戻り値がInt.maxを超えてしまう可能性があるため、超えてしまった場合はエラーを発生させる
func sam(_ int: Int) throws -> Int {
guard int <= Int.max / 3 else {
throw SamError.overCapacity
}
return int * 10
}
//引数に9を与えているためエラーが発生しないことは明らかなためtry!キーワードを使用して、
//エラー処理を実装せずに関数を呼び出しています。
let tripleOfInt = try! sam(9) // do-catch文なしで実行可能
print(tripleOfInt) //90
try?キーワード
throwsキーワードが指定された処理であっても、利用時にはエラーの詳細が不要な場合もあります。このようなケースではtry?キーワードを使用できます。
try?キーワードを付けてthrowsキーワードが指定された処理(関数)を呼び出すと関数の戻り値がOptional<Wrapped>型となります。
enum SamError : Error {
case overCapacity
}
//戻り値がInt.maxを超えてしまう可能性があるため、超えてしまった場合はエラーを発生させる
func sam(_ int: Int) throws -> Int {
guard int <= Int.max / 3 else {
throw SamError.overCapacity
}
return int * 10
}
//引数に9を与えているためエラーが発生しないことは明らかなためtry?キーワードを使用して、
//エラー処理を実装せずに関数を呼び出しています。
if let val = try? sam(9){ // do-catch文なしで実行可能
print(val) //90
}
//“関数の戻り値はOptional<Wrapped>型
print(try? sam(9)) //Optional(90)
イニシャライザに使用
エラーを発生させるイニシャライザに対しても利用できるので、失敗可能イニシャライザのように扱うこともできます。
enum SamError: Error {
case outOfRange
}
struct Sam {
let val: Int
init(_ val: Int) throws {
guard case 13...19 = val else {
throw SamError.outOfRange
}
self.val = val
}
}
if let a = try? Sam(17) {
print(a) //Sam(val: 17)
}
enum CustomErr: Error{
case err1(Int)
case err2(Cerr)
}
struct Cerr{
let a:String!
init(_ a:String){
self.a = a
}
func sam(){
print(a)
}
}
func sam(_ a:Int)throws ->Int{
let b = Cerr("over")
guard a<10 else {
throw CustomErr.err2(b)
}
return a
}
do {
try sam(15)
} catch CustomErr.err2(let val) {
val.sam()
}