わけあって、 swift-nio-ssl を急遽触っているが、その際に Swift5 の Result
型をさわったので、そのメモ。
ソースコードはGitHubにあるので、この説明よりはソースを読むほうが速い。
型の詳細
Result
のコードを見てみると次のように定義されています。
public enum Result<Success, Failure: Error> { case success(Success) case failure(Failure) }
これは、 haskell の Either l r
のように、 Success
または Failure: Error
のどちらか一方を保持可能な型
ハンドリング
swift-nio の NIO2 では EventLoopFuture<Value>
に whenComplete((Result<Value,Error>) -> Void)
というメソッドが追加されており、成功であろうが、失敗であろうがハンドリングが可能になっている。というわけで、こちらを例にエラーハンドリングを行う。
let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) let promise: EventLoopPromise<String> = eventLoopGroup.next().makePromise(String.self) promise.futureResult.whenComplete({ result in switch result { case let .success(item): print(item) case let .failure(err): print(err) }) promise.succeed("hello")
これを実行すると次のように表示される。
hello
また、次のように Error
を渡せる。
enum Err: Error { case err(_ message: String) } let promise: EventLoopPromise<String> = eventLoopGroup.next().makePromise(String.self) promise.futureResult.whenComplete({ result in switch result { case let .success(item): print(item) case let .failure(err): print(err) }) promise.fail(Err.err("hello"))
これを実行すると次のように表示される。
err("hello")
map
/mapError
/flatMap
/flatMapError
Result
には map
/mapError
/flatMap
/flatMapError
というメソッドがついている。
map
map
は Result<Success, Failure>
の Success
を異なる NewSuccess
に変換するメソッド。
promise.futureResult.whenComplete({ result in let r = result.map { (str: String) -> Int in str.utf8.count } print(r) }) promise.succeed("foo-bar")
これは次のように表示される
success(7)
flatMap
flatMap
は Result<Success, Failure>
の Success
を別の Result<NewSuccess, Failure>
に変換するメソッド。
enum Err: Error { case err(_ message: String) } enum Err2: Error { case err2(String) } let result: Result<String, Error> = Result.success("foo") let r = result.flatMap { (str: String) -> Result<Int, Error> in .failure(Err2.err2(str)) }
このコードの実行結果は次のようになる
failure(MyModule.Err2.err2("foo"))
なお、新しい Result<NewSuccess, Failure>
の Failure
の方は型を変えられないので注意が必要。例えば、次のコードはエラーとなる。
enum Err: Error { case err(String) } enum Err2: Error { case err2(String) } let result: Result<String, Err> = .success("foo") let r = result.flatMap { (str: String) -> Result<Int, Error> in .failure(Err2.err2(str)) }
結果
main.swift:10:24: error: cannot convert value of type '(String) -> Result<Int, Error>' to expected argument type '(String) -> Result<_, Err>' let r = result.flatMap { (str: String) -> Result<Int, Error> in .failure(Err2.err2(str)) } ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
mapError
mapError
は map
の Failure
版。 Failure
を別の異なる NewFailure
に変換する
enum Err: Error { case err(_ message: String) } enum Err2: Error { case err2(String) } let result: Result<String, Err> = Result.failure(.err("foo")) let r = result.mapError { (e: Err) -> Err2 in switch e { case let .err(msg): return .err2(msg) } }
実行結果
failure(MyModule.Err2.err2("foo"))
flatMapError
flatMapError
は flatMap
の Failure
版。 Success
の型は変えられない。
enum Err: Error { case err(_ message: String) } enum Err2: Error { case err2(String) } p2.succeed("foo-bar") let result: Result<String, Err> = Result.failure(.err("foo")) let r = result.flatMapError { (e: Err) -> Result<String, Err2> in switch e { case let .err(msg): return .success(msg) } }
実行結果
success("foo")
その他
Success
を取り出すか、Failure
を投げるget
というメソッドがある。let result: Result<String, Error> = Result(catching: { "foo" })
というイニシャライザーがある。
おわり