Swift5のResultが楽しそうだったので、色々遊んでみました。
Xcode10.2になって、playgroundの挙動も改善してますね。以前は怪しい挙動をよく見かけましたが、今のところ大丈夫そう。
Appleの公式リファレンスはこちらになります。
https://developer.apple.com/documentation/swift/result
0.この関数を使って遊んだ
よく使う感じの、結果をクロージャで返す関数です。即時実行されるので、escaping
はしません。
import UIKit
struct EmptyError: Error { }
extension EmptyError: CustomStringConvertible {
var description: String {
return "value is Empty!"
}
}
// 関数定義
func resultTest(value: String, completion: (Result<String, Error>) -> Void) {
if !value.isEmpty {
completion(.success("Aurgument you pass is \(value)"))
} else {
completion(.failure(EmptyError()))
}
}
#1. いつも通りのResultで遊んだ
switch
で書き分けるいつもの方法です。いつもどおり使えるのは大事です。
// 準正常系テスト、いつも通りのResultの使い方
resultTest(value: "") { (result) in
switch result {
case .success(let value):
print(value)
case .failure(let error):
print(error)
}
}
// output: value is Empty!
2. get()で遊んだ
Swift5で追加された Result
にはget()メソッドがあり、resultを do/catch
に変換できます。manual prop
から auto prop
への変換メソッドとも言えるような気がします。
参考: https://qiita.com/koher/items/7e92414082476fb87b76#manual-propagation-と-automatic-propagation
// 正常系
resultTest(value: "テストだよん!") { (result) in
do {
print(try result.get())
} catch {
print(error)
}
}
// output: Aurgument you pass is テストだよん!
3. mapで遊んだ
新しく使えるようになったmap(_:)メソッドでは、結果がsuccessの場合に関数適応した新しいResultを返せます。この場合は、成功時の値が hoge
のReusult型から、その後に fuga
を加えて hoge fuga
を持つResultを新しく返しています。
他にもmapError(_:), flatMap(_:), flatMapError(_:)などの親戚がいます。
resultTest(value: "hoge") { (result) in
let newResult = result.map { $0 + " fuga" }
switch newResult {
case .success(let value):
print(value)
case .failure(let error):
print(error)
}
}
// output: Aurgument you pass is hoge fuga
#4. 初期化で遊んだ
init(catching: )
というイニシャライザがResult
には用意されており、クロージャ内で return
するとsuccess、throw
するとfailureとしてResult
を初期化できます。
いまいちユースケースが思いつかないので、詳しい方コメント頂けると助かります!
var optionalInt: Int? = 2
let result = Result<Int, Error> { () -> Int in
guard let int = optionalInt else {
throw NSError(domain: "OptionalError", code: 111, userInfo: nil) // 適当に返してる
}
return int
}
print(try! result.get())
// output: 2
#5. Equatableで遊んだ
条件付き適合として以下のように定義されており、SuccessとFailureの両方がEquatableに適合している場合は、Result自体が同一視可能になります。(Hashableも同様の実装がされています。
extension Result : Equatable where Success : Equatable, Failure : Equatable { }
適当なエラーを作って Equatable
に適合させてみると、比較が出来るようになりました。
struct UnderZeroError: Error {
let value: Int
var localizedDescription: String {
return "\(value) is under zero."
}
}
extension UnderZeroError: Equatable { }
let comparableResultOne: Result<Int, UnderZeroError> = .success(3)
let comparableResultTwo: Result<Int, UnderZeroError> = .success(3)
print(comparableResultOne == comparableResultTwo)
// output: true
let comparableFailureResultOne: Result<Int, UnderZeroError> = .failure(UnderZeroError(value: -2))
let comparableFailureResultTwo: Result<Int, UnderZeroError> = .failure(UnderZeroError(value: -3))
print(comparableFailureResultOne == comparableFailureResultTwo)
// output: false
6. 遊んだ感想
機能が多すぎず足りなすぎず、公式対応として ちょうどいい感じのResult が提供されたように感じます。
async/await早く使いたい。
終
制作・著作
━━━━━
ⓃⒽⓀ