7-13.【高级特性】Never 类型在泛型和控制流中有什么作用?

27 阅读4分钟

Never 是 Swift 中一个非常特殊的存在,它被称为 "Bottom Type"(底类型) 。它的定义非常简单,但也非常霸道:它是一个没有成员的枚举(Empty Enum)

这意味着你永远无法实例化一个 Never 类型的值。正是这种“无法存在”的特性,让它在控制流和泛型中发挥了巨大的逻辑作用。


1. 在控制流中的作用:告知编译器“此路不通”

Never 出现之前,执行必死无疑的操作(如 fatalError)返回的是 Void。但 Void 实际上是有值的(即 ()),这会让编译器认为函数执行完后还会继续往下走。

而返回 Never 的函数被称为 Non-returning Function(永不返回的函数)

优化逻辑分支

如果你在 guard 语句或 switch 中调用了一个返回 Never 的函数,编译器知道程序在此处会终止,因此不需要你在后面写 returnbreak

Swift

func crashAndBurn() -> Never {
    fatalError("程序在此处终结")
}

func test(input: Int?) {
    guard let value = input else {
        crashAndBurn() // 编译器知道这里不会返回,所以不需要 return
    }
    print(value) // 只有成功时才会执行
}

2. 在泛型中的作用:表示“不可能发生的情况”

这是 Never 最精妙的用法。它常被用作泛型占位符,用来在类型层面上关闭某种可能性。

A. Result<Success, Failure>

如果你有一个异步任务,逻辑上它永远不会失败(比如从内存读取配置),你可以这样定义:

Swift

let successOnly: Result<String, Never> = .success("Everything is fine")

因为 Failure 类型是 Never,而 Never 无法产生实例,所以你永远无法构造出一个 .failure 情况。这让处理结果时非常清爽:

Swift

switch successOnly {
case .success(let message):
    print(message)
// 不需要写 case .failure,编译器知道它根本不存在
}
B. Combine 中的 Publisher

在 Combine 框架中,如果一个发布者永远不会抛出错误,它的 Failure 类型就会被设为 Never。这允许你使用 .assign(to:on:) 等要求不报错的操作符。


3. 本质:为什么它是 Empty Enum?

在 Swift 中,Never 的底层实现就是一个不带任何 case 的枚举:

Swift

public enum Never {}

根据 Swift 的规则:

  1. 枚举是值类型。
  2. 一个没有 case 的枚举无法被初始化
  3. 因此,任何声明为 Never 的变量都无法被赋予真实的值。

这在类型论中非常完美:Any 是类型的顶峰(包含所有),Never 是底端(不包含任何)。


4. 总结对比

场景使用 Never 的效果
函数返回告知编译器该点之后的代码不可达(Unreachable),消除多余的 return
泛型约束逻辑上消除某种状态(如“绝不报错”或“没有数据”)。
协议实现如果某个协议要求一个关联类型,而你当前的实现不需要它,可以用 Never 填充。

英文版

7-13. [Advanced] What is the role of the Never type in Generics and Control Flow?

Never is a highly unique entity in Swift, known in Type Theory as the "Bottom Type." Its definition is incredibly simple yet absolute: it is an empty enumeration (an enum with no cases).

This means you can never instantiate a value of type Never. It is precisely this "impossibility of existence" that allows it to perform critical logical functions within control flow and generics.


1. Role in Control Flow: Informing the Compiler of "Dead Ends"

Before Never was introduced, operations that inevitably terminated the process (like fatalError) returned Void. However, Void actually has a value (the empty tuple ()), which leads the compiler to assume that execution could potentially continue after the function call.

A function that returns Never is known as a Non-returning Function.

Optimizing Logical Branches

If you call a function returning Never inside a guard statement or a switch case, the compiler understands that the program terminates at that point. Consequently, it does not require a return, break, or throw following that call.

Swift

func crashAndBurn() -> Never {
    fatalError("The program ends here")
}

func test(input: Int?) {
    guard let value = input else {
        crashAndBurn() // The compiler knows this doesn't return; no 'return' needed.
    }
    print(value) // This only executes upon success
}

2. Role in Generics: Representing "Impossible Scenarios"

This is the most elegant application of Never. It is frequently used as a generic placeholder to logically disable a specific possibility at the type level.

A. Result<Success, Failure>

If you have an asynchronous task that, by design, can never fail (such as reading a configuration already loaded in memory), you can define it as follows:

Swift

let successOnly: Result<String, Never> = .success("Everything is fine")

Because the Failure type is Never, and Never cannot be instantiated, it is impossible to construct a .failure case. This makes handling the result much cleaner:

Swift

switch successOnly {
case .success(let message):
    print(message)
// No need for a .failure case; the compiler knows it cannot exist.
}
B. Publisher in Combine

In the Combine framework, if a publisher is guaranteed never to emit an error, its Failure type is set to Never. This allows you to use operators like .assign(to:on:), which require a non-failing stream.


3. The Essence: Why an Empty Enum?

In Swift, the underlying implementation of Never is an enum with no cases:

Swift

public enum Never {}

According to Swift's rules:

  1. Enums are value types.
  2. An enum with no cases cannot be initialized.
  3. Therefore, no variable declared as Never can ever hold an actual value.

In Type Theory, this represents a perfect hierarchy: Any is the Top Type (containing everything), and Never is the Bottom Type (containing nothing).


4. Summary Table

ScenarioEffect of using Never
Function ReturnInforms the compiler that subsequent code is unreachable, eliminating redundant return statements.
Generic ConstraintsLogically eliminates a state (e.g., "guaranteed no error" or "no data").
Protocol ImplementationIf a protocol requires an associated type that your specific implementation doesn't need, use Never as a filler to disable that path.