美文网首页swift
巧用Swift Never

巧用Swift Never

作者: CHEwz | 来源:发表于2018-08-06 11:39 被阅读0次

    Ref: Never-Mattt

    本文是在阅读Mattt作者后写的。

    Never一开始是用来取代Swift3之前的@noreturn关键字的。它本身只是一个enum,如果作为一个函数的返回值就代表这个函数永远不会返回,适用于一个函数需要不断循环做一些事情,比如iOS的事件循环等。

    /// 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

    我们一般会把它作为函数的返回值(貌似也不怎么用到Never),其实也有一些其它用法。巧妙地把Never用于传参中会有意想不到的效果。

    Swift没有一个标准的Result类型,但大致和下面的描述差不多:

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

    Result类型包含了valueserrors,一般用于异步的网络请求中,例如:

    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)")
        }
    }
    

    现在,我们考虑这样一种情况,一个函数在completion的handler里总是返回成功(success)的结果,也就没必要写failure的case分支了,但是默认情况下,我们是需要写failure分支的。现在我们利用Never可以做到不用写它了:

    func alwaysSucceeds(_ completion: (Result<String, Never>) -> Void) {
        completion(.success("yes!"))
    }
    
    alwaysSucceeds { (result) in
        switch result {
        case .success(let string):
            print(string)
        }
    }
    

    要让Nerve支持以上写法,需要实现两个协议:Swift.Error和Comparable。

    1.遵从Error协议,使得Nerve能作为Result类型的模板参数。

    extension Never: Error {
        // 刚好Error是一个空协议,不用再多写什么
    }
    

    2.遵从Comparable协议,使得在对Result对象做switch的时候可比较以跳转正确的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协议只需要实现以上3个,其它函数由编译器自动补全。

    完整代码如下:

    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在阅读上更加友好。

    ??运算符和Nerve

    ??运算符又叫空合并运算符。需要两个操作数,当lhs为空的时候就使用rhs。

    强制解包(unwrap)运算符(!)是一种危险的操作,很容易引起程序崩溃。

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

    array为空,强制解包就会引起崩溃。

    所以为了避免没必要的崩溃,我们一般会用if-let or guard-let去解包。

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

    如果你知道为空的时候需要抛出异常,或者就是需要结束程序的时候,这样写就有点不太优雅。我们可以利用??和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
    

    当然,如果以后Never作为swift的基础底层类型的时候,可能会自动支持作为??的rhs,就不需要我们去实现func ??<T>(lhs: T?, rhs: @autoclosure () -> Never) -> T

    原文还有两个关于Never在未来使用的展望:throw表达式和throws<Never>标记。不过我看来没啥必要,有兴趣的朋友可以去原文看看。

    相关文章

      网友评论

        本文标题:巧用Swift Never

        本文链接:https://www.haomeiwen.com/subject/tlvwvftx.html