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
类型包含了values
和errors
,一般用于异步的网络请求中,例如:
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>
标记。不过我看来没啥必要,有兴趣的朋友可以去原文看看。
网友评论