見出し画像

楽しい!Swift。 - A Swift Tour (Error Handling & Generics)

Error Handling

Error プロトコルに準拠することで、任意の型でエラーを表現することができます。

enum PrinterError: Error {
    case outOfPaper
    case noToner
    case onFire
}

enum PrinterError: Error

エラーについて宣言することができます。

実行したい関数に throws を付けます。

エラーをありそうなところに throw をつけておくと、もしエラーが発生した場合にはthrow以下のコードがすぐに実行されます。

func send(job: Int, toPrinter printerName: String) throws -> String {
    if printerName == "Never Has Toner" {
        throw PrinterError.noToner
    }
    return "Job sent"
}

func send(job: Int, toPrinter printerName: String) throws -> String {

throw PrinterError.noToner

このエラーがの可能性がある関数を実行する方法としては do-catch を使います。

do ブロックの中で、エラーの可能性がある箇所の前に try を付けて使うと、catch ブロックで指定しなければ、error という名前で自動的にエラー情報が出ます

do {
    let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")
    print(printerResponse)
} catch {
    print(error)
}

"Bi Sheng"の代わりに"Never Has Toner"を入れるとエラーになり"noToner"となります。enumで宣言しているcase noTonerが呼び出されています。


特定のエラーでの処理をするために、複数の catch ブロックで定義することができます。switch 文の case のように catch の後ろに処理させたいことを書くことでいろんな処理ができるようになります。

do {
    let printerResponse = try send(job: 1440, toPrinter: "Gutenberg")
    print(printerResponse)
} catch PrinterError.onFire {
    print("I'll just put this over here, with the rest of the fire.")
} catch let printerError as PrinterError {
    print("Printer error: \(printerError).")
} catch {
    print(error)
}
// Prints "Job sent"

この場合に"Gutenberg"の代わりに"Never Has Toner"を入れて実行すると

catch let printerError as PrinterError {
 print("Printer error: \(printerError).")}

が呼び出されて"Printer error: noToner."と出力されます。

エラーを処理するもう 1 つの方法は、try? を付けて結果をオプショナルに変換することです。もしその関数がエラーがある場合、特定のエラーは破棄されて、結果が nil になります。

そうでなければ、結果は、関数が返す値を内包したオプショナル値になります。

let printerSuccess = try? send(job: 1884, toPrinter: "Mergenthaler")
let printerFailure = try? send(job: 1885, toPrinter: "Never Has Toner")

これを実行すると

printerSuccess ・・・ Job sent
printerFailure ・・・ nil

となります。

関数内の全ての処理の実行後、関数が結果を返す直前にコードを実行したい場合、defer を使います。このブロックは関数がエラーがある場合でも実行されます。

var fridgeIsOpen = false
let fridgeContent = ["milk", "eggs", "leftovers"]


func fridgeContains(_ food: String) -> Bool {
    fridgeIsOpen = true
    defer {
        fridgeIsOpen = false
    }


    let result = fridgeContent.contains(food)
    return result
}
if fridgeContains("banana") {
    print("Found a banana")
}
print(fridgeIsOpen)


Generics

ジェネリックな関数や型を作成するには、山括弧(<>)の中に名前を書きます。ジェネリックとは型を気にせず使えるようにする一つの方法です。

func makeArray<Item>(repeating item: Item, numberOfTimes: Int) -> [Item] {
    var result: [Item] = []
    for _ in 0..<numberOfTimes {
         result.append(item)
    }
    return result
}
makeArray(repeating: "knock", numberOfTimes: 4)

func makeArray<Item>(repeating item: Item, numberOfTimes: Int) -> [Item]

クラス、列挙型、構造体と同じように、ジェネリックな関数やメソッドも作成することができます。

// Reimplement the Swift standard library's optional type
enum OptionalValue<Wrapped> {
    case none
    case some(Wrapped)
}
var possibleInteger: OptionalValue<Int> = .none
possibleInteger = .some(100)

要件の一覧を指定するために、本文の直前に where を使います。

例えば、プロトコルを実装するために型が必要な場合や、2 つの型が一致している必要がある場合、クラスが特定のスーパークラスを継承している必要がある場合などに使います。

func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> Bool
    where T.Element: Equatable, T.Element == U.Element
{
    for lhsItem in lhs {
        for rhsItem in rhs {
            if lhsItem == rhsItem {
                return true
            }
        }
    }
   return false
}
anyCommonElements([1, 2, 3], [3])

<T: Equatable> は <T> ... where T: Equatable と同じです。


この記事が気に入ったらサポートをしてみませんか?