わけあって、 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" })というイニシャライザーがある。
おわり