巧用Swift Never

Ref: Never-Mattt

本文是在閱讀Mattt作者后寫的。

Never一開始是用來取代Swift3之前的@noreturn關(guān)鍵字的抄瓦。它本身只是一個enum呢诬,如果作為一個函數(shù)的返回值就代表這個函數(shù)永遠(yuǎn)不會返回,適用于一個函數(shù)需要不斷循環(huán)做一些事情,比如iOS的事件循環(huán)等育苟。

/// The return type of functions that do not return normally, that is, a type
/// with no values.
///
/// Use `Never` as the return type when declaring a closure, function, or
/// method that unconditionally throws an error, traps, or otherwise does
/// not terminate.
///
///     func crashAndBurn() -> Never {
///         fatalError("Something very, very bad happened")
///     }
public enum Never {
}

switch和Never

我們一般會把它作為函數(shù)的返回值(貌似也不怎么用到Never),其實(shí)也有一些其它用法椎木。巧妙地把Never用于傳參中會有意想不到的效果违柏。

Swift沒有一個標(biāo)準(zhǔn)的Result類型,但大致和下面的描述差不多:

enum Result<Value, Error: Swift.Error> {
    case success(Value)
    case failure(Error)
}

Result類型包含了valueserrors香椎,一般用于異步的網(wǎng)絡(luò)請求中漱竖,例如:

func fetch(_ request: Request, completion: (Result<Response, Error>) -> Void) {
    // ...
}

fetch(request) { result in
    switch result {
    case .success(let value):
        print("Success: \(value)")
    case .failure(let error):
        print("Failure: \(error)")
    }
}

現(xiàn)在,我們考慮這樣一種情況畜伐,一個函數(shù)在completion的handler里總是返回成功(success)的結(jié)果馍惹,也就沒必要寫failure的case分支了,但是默認(rèn)情況下玛界,我們是需要寫failure分支的⊥蚍現(xiàn)在我們利用Never可以做到不用寫它了:

func alwaysSucceeds(_ completion: (Result<String, Never>) -> Void) {
    completion(.success("yes!"))
}

alwaysSucceeds { (result) in
    switch result {
    case .success(let string):
        print(string)
    }
}

要讓Nerve支持以上寫法,需要實(shí)現(xiàn)兩個協(xié)議:Swift.Error和Comparable慎框。

1.遵從Error協(xié)議良狈,使得Nerve能作為Result類型的模板參數(shù)。

extension Never: Error {
    // 剛好Error是一個空協(xié)議笨枯,不用再多寫什么
}

2.遵從Comparable協(xié)議薪丁,使得在對Result對象做switch的時(shí)候可比較以跳轉(zhuǎn)正確的case分支。

extension Never: Comparable {
    public static func < (lhs: Never, rhs: Never) -> Bool {
        switch (lhs, rhs) {}
    }

    public static func > (lhs: Never, rhs: Never) -> Bool {
        switch (lhs, rhs) {}
    }

    public static func == (lhs: Never, rhs: Never) -> Bool {
        switch (lhs, rhs) {}
    }
}

Comparable協(xié)議只需要實(shí)現(xiàn)以上3個馅精,其它函數(shù)由編譯器自動補(bǔ)全严嗜。

完整代碼如下:

extension Never: Error {

}

extension Never: Comparable {
    public static func < (lhs: Never, rhs: Never) -> Bool {
        switch (lhs, rhs) {}
    }

    public static func > (lhs: Never, rhs: Never) -> Bool {
        switch (lhs, rhs) {}
    }

    public static func == (lhs: Never, rhs: Never) -> Bool {
        switch (lhs, rhs) {}
    }
}

enum Result<Value, Error: Swift.Error> {
    case success(Value)
    case failure(Error)
}

func alwaysSucceeds(_ completion: (Result<String, Never>) -> Void) {
    completion(.success("yes!"))
}

alwaysSucceeds { (result) in
    switch result {
    case .success(let string):
        print(string)
    }
}

但是個人覺得這種方式不如用if-case-let寫法來得直接:

if case .success(let value) = result {
    // Do something
}

但從語義上來說switch-Nerve在閱讀上更加友好。

??運(yùn)算符和Nerve

??運(yùn)算符又叫空合并運(yùn)算符硫嘶。需要兩個操作數(shù)阻问,當(dāng)lhs為空的時(shí)候就使用rhs梧税。

強(qiáng)制解包(unwrap)運(yùn)算符(!)是一種危險(xiǎn)的操作沦疾,很容易引起程序崩潰。

let array: [Int] = []
let firstIem = array.first!

array為空第队,強(qiáng)制解包就會引起崩潰哮塞。

所以為了避免沒必要的崩潰,我們一般會用if-let or guard-let去解包凳谦。

let array: [Int] = []
guard let firstItem = array.first else {
    fatalError("array cannot be empty")
}

如果你知道為空的時(shí)候需要拋出異常忆畅,或者就是需要結(jié)束程序的時(shí)候,這樣寫就有點(diǎn)不太優(yōu)雅尸执。我們可以利用??和Never:

func ?? <T>(lhs: T?, rhs: @autoclosure () -> Never) -> T {
    switch lhs {
    case let value?:
        return value
    case nil:
        rhs()
    }
}

let firstItem = array.first ?? fatalError("array cannot be empty")
// Fatal error: array cannot be empty

當(dāng)然家凯,如果以后Never作為swift的基礎(chǔ)底層類型的時(shí)候缓醋,可能會自動支持作為??的rhs,就不需要我們?nèi)?shí)現(xiàn)func ??<T>(lhs: T?, rhs: @autoclosure () -> Never) -> T

原文還有兩個關(guān)于Never在未來使用的展望:throw表達(dá)式和throws<Never>標(biāo)記绊诲。不過我看來沒啥必要送粱,有興趣的朋友可以去原文看看。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末掂之,一起剝皮案震驚了整個濱河市抗俄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌世舰,老刑警劉巖动雹,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異跟压,居然都是意外死亡胰蝠,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進(jìn)店門震蒋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來姊氓,“玉大人,你說我怎么就攤上這事喷好∠韬幔” “怎么了?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵梗搅,是天一觀的道長禾唁。 經(jīng)常有香客問我,道長无切,這世上最難降的妖魔是什么荡短? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮哆键,結(jié)果婚禮上掘托,老公的妹妹穿的比我還像新娘。我一直安慰自己籍嘹,他們只是感情好闪盔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著辱士,像睡著了一般泪掀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上颂碘,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天异赫,我揣著相機(jī)與錄音,去河邊找鬼。 笑死塔拳,一個胖子當(dāng)著我的面吹牛鼠证,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播靠抑,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼名惩,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了孕荠?” 一聲冷哼從身側(cè)響起娩鹉,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎稚伍,沒想到半個月后弯予,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡个曙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年锈嫩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片垦搬。...
    茶點(diǎn)故事閱讀 39,745評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡呼寸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出猴贰,到底是詐尸還是另有隱情对雪,我是刑警寧澤,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布米绕,位于F島的核電站瑟捣,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏栅干。R本人自食惡果不足惜迈套,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望碱鳞。 院中可真熱鬧桑李,春花似錦、人聲如沸窿给。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽填大。三九已至戒洼,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間允华,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留靴寂,地道東北人磷蜀。 一個月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像百炬,于是被迫代替她去往敵國和親褐隆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評論 2 354

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

  • # Python關(guān)鍵字 部分單詞沒有分類整理按照順序整理的 ``` statements語句 print輸出 qu...
    ZhouLang閱讀 593評論 0 0
  • 1剖踊、隨機(jī)數(shù) 不需要隨機(jī)數(shù)種子 arc4random()%N + begin:產(chǎn)生begin~begin+N的隨機(jī)數(shù)...
    我是小胡胡123閱讀 4,161評論 0 2
  • C++運(yùn)算符重載-上篇 本章內(nèi)容:1. 運(yùn)算符重載的概述2. 重載算術(shù)運(yùn)算符3. 重載按位運(yùn)算符和二元邏輯運(yùn)算符4...
    Haley_2013閱讀 2,296評論 0 51
  • 問題 1)柯里化庶弃,通過柯里化,改造target-action德澈,因?yàn)閟elector只能使用字符串歇攻,在編譯時(shí)無法發(fā)現(xiàn)...
    lanjing閱讀 3,502評論 3 19
  • 文件 什么是文件 文件是數(shù)據(jù)存儲的單位 文件通常用來長期存儲數(shù)據(jù) 文件中的數(shù)據(jù)是以字節(jié)為單位進(jìn)行順序存儲的 文件的...
    遇明不散閱讀 277評論 0 0