Swift 5.0 值得關(guān)注的特性:增加 Result<T, E: Error> 枚舉類型

HackingSwift: What’s new in Swift 5.0
Result<T> 還是 Result<T, E: Error>

背景

在異步獲取數(shù)據(jù)的場景中,常見的回調(diào)的數(shù)據(jù)結(jié)構(gòu)是這樣的:表示獲取成功的數(shù)據(jù),表示獲取失敗的 error。因?yàn)閿?shù)據(jù)可能獲取成功苗缩,也可能失敗。因此回調(diào)中的數(shù)據(jù)和錯(cuò)誤都是 optional 類型。
比如 CloudKit 中保存數(shù)據(jù)的一個(gè)函數(shù)就是這樣:

func save(_ record: CKRecord, completionHandler: @escaping (CKRecord?, Error?) -> Void)

這種形式的缺點(diǎn)是沒有體現(xiàn)出兩種結(jié)果的互斥關(guān)系:如果數(shù)據(jù)成功獲取到了灶挟,那么 error 一定為空稚铣。如果 error 有值算色,數(shù)據(jù)一定是獲取失敗了。

Swift 中枚舉的能力相比 OC 有著很大的進(jìn)步,每個(gè)枚舉值除了可以是常規(guī)的基礎(chǔ)類型牡肉,還可以是一個(gè)關(guān)聯(lián)的類型捧灰。有了這樣的特性后用枚舉來優(yōu)化返回結(jié)果的數(shù)據(jù)結(jié)構(gòu)顯得水到渠成:

enum Result<Success, Failure> where Failure : Error {

    /// A success, storing a `Success` value.
    case success(Success)

    /// A failure, storing a `Failure` value.
    case failure(Failure)
}

基本用法

定義異步返回結(jié)果是 Int 類型的函數(shù):

func fetchData(_ completionHandler: @escaping (Result<Int, Error>) -> Void) {
    DispatchQueue.global().async {
        let isSuccess = true
        if isSuccess {
            let resultValue = 6
            return completionHandler(.success(resultValue))
        } else {
            let error = NSError(domain: "custom error", code: -1, userInfo: nil)
            return completionHandler(.failure(error))
        }
    }
}

返回值的類型通過泛型進(jìn)行約束,Result 第一個(gè)泛型類型表示返回值的類型,第二個(gè)類型表示錯(cuò)誤的類型毛俏。對 Result 賦值和常規(guī)的枚舉一樣:

let valueResult: Result<Int, CustomError> = Result.success(4)

// 因?yàn)?swift 中會進(jìn)行類型推斷炭庙,編譯器在確認(rèn)返回的是 `Result` 類型后,可以省略枚舉類型的聲明
let errorResult = .failure(CustomError.inputNotValid)

取出 Result 值和獲取普通的關(guān)聯(lián)類型枚舉是一樣的:

fetchData { (result) in
    switch result {
    case .success(let value):
        print(value)
    case .failure(let error)
        print(error.localizedDescription)
    }
}

如果你只想要獲取其中一項(xiàng)的值煌寇,也可以直接用 if case 拆包:

fetchDate { (result) in
    if case .success(let value) = result {
        print(value)
    }
}

可以判等

Enum 是一個(gè)值類型焕蹄,是一個(gè)值就應(yīng)該可以判斷是否相等。如果 Result 的成功和失敗的類型都是 Equatable阀溶,那么 Result就可以判等腻脏,源碼如下:

extension Result : Equatable where Success : Equatable, Failure : Equatable { }

類似的,如果是成功和失敗的類型都是 Hashable银锻,那么 Result 也是 Hashable

extension Result : Hashable where Success : Hashable, Failure : Hashable { }

如果實(shí)現(xiàn)了 Hashable 永品,可以用來當(dāng)做字典的 key。

輔助的 API

map击纬、mapError

與 Dictionary 類似鼎姐,Swift 為 Result 提供了幾個(gè) map value 和 error 的方法。

let intResult: Result<Int, Error> = Result.success(4)
let stringResult = x.map { (value) -> Result<String, Error> in
    return  .success("map")
}

let originError = NSError(domain: "origin error", code: -1, userInfo: nil)
let errorResult: Result<Int, Error> = .failure(originError)
let newErrorResult = errorResult.mapError { (error) -> Error in
    let newError = NSError(domain: "new error", code: -2, userInfo: nil)
    return newError
}

flatMap更振、flatMapError

map 返回的是具體的結(jié)果和錯(cuò)誤炕桨, flatMap 閉包中返回的是 Result 類型。如果 Result 中包含的是數(shù)據(jù)肯腕,效果和 map 一致献宫,替換數(shù)據(jù);如果 Result 中包含的是錯(cuò)誤实撒,那么不替換結(jié)果姊途。

let intResult: Result<Int, Error> = Result.success(4)

// 替換成功
let flatMapResult = intResult.flatMap { (value) -> Result<String, Error> in
    return  .success("flatMap")
}

// 沒有執(zhí)行替換操作,flatMapIntResult 值還是 intResult
let flatMapIntResult = intResult.flatMap { (value) -> Result<String, Error> in
    return  .failure(NSError(domain: "origin error", code: -1, userInfo: nil))
}

get

很多時(shí)候只關(guān)心 Result 的值知态,Swift 提供了 get() 函數(shù)來便捷的直接獲取值吭净,需要注意的是這個(gè)函數(shù)被標(biāo)記為 throws,使用時(shí)語句前需要加上 try

let intResult: Result<Int, Error> = Result.success(4)

let value = try? intResult.get()

可拋出異常的閉包初始化器

很多時(shí)候獲取返回值的閉包中可能會發(fā)生異常代表獲取失敗的錯(cuò)誤肴甸,基于這個(gè)場景 Swift 提供了一個(gè)可拋出異常的閉包初始化器:

enum CustomError: Error, Equatable {
    case inputNotValid
}

let fetchInt = { () -> Int in
    if true {
        return 4
    } else {
        throw CustomError.inputNotValid
    }
}

let result: Result<Int, Error> = Result { try fetchInt() }

需要提醒是通過這種方式聲明的 Result 的 error 類型只能是 Error,不能指定特定的 Error囚巴。


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末原在,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子彤叉,更是在濱河造成了極大的恐慌庶柿,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件秽浇,死亡現(xiàn)場離奇詭異浮庐,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進(jìn)店門审残,熙熙樓的掌柜王于貴愁眉苦臉地迎上來梭域,“玉大人,你說我怎么就攤上這事搅轿〔≌牵” “怎么了?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵璧坟,是天一觀的道長既穆。 經(jīng)常有香客問我,道長雀鹃,這世上最難降的妖魔是什么幻工? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮黎茎,結(jié)果婚禮上囊颅,老公的妹妹穿的比我還像新娘。我一直安慰自己工三,他們只是感情好迁酸,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著俭正,像睡著了一般奸鬓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上掸读,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天串远,我揣著相機(jī)與錄音,去河邊找鬼儿惫。 笑死澡罚,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的肾请。 我是一名探鬼主播留搔,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼铛铁!你這毒婦竟也來了隔显?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤饵逐,失蹤者是張志新(化名)和其女友劉穎括眠,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體倍权,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡掷豺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片当船。...
    茶點(diǎn)故事閱讀 39,902評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡题画,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出生年,到底是詐尸還是另有隱情婴程,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布抱婉,位于F島的核電站档叔,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蒸绩。R本人自食惡果不足惜衙四,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望患亿。 院中可真熱鬧传蹈,春花似錦、人聲如沸步藕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽咙冗。三九已至沾歪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間雾消,已是汗流浹背灾搏。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留立润,地道東北人狂窑。 一個(gè)月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像桑腮,于是被迫代替她去往敵國和親泉哈。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容