Swift 模式匹配的一个有趣的问题

昨天 Chris Eidhof 大神在 Tweets 上发了一段这样的代码,他认为这是 Swift 的一个 bug 。推文

CvSZd_ZW8AEYkgm.jpg-large

最近在看喵神的《Swifter - Swift 必备 Tips(第三版)》,刚好看到字面量表达模式匹配。所以也想尝试解释这个 bug 的产生原因。

字面量表达

什么是字面量?在 Swift 中我们会像下面那样赋值变量,这里的 3、Hello、true 就是字面量。

1
2
3
let aNumber = 3
let aString = "Hello"
let aBool = true

在上面的代码中 HasPrefix 这个 struct 继承了 ExpressibleByStringLiteral 这个协议,使得它支持了 字面量表达
也就是说现在 HasPrefix 可以像下面这样赋值了:

模式匹配

关于模式匹配的使用我这里就不做解释了,打开可以看 SwiftGG 上的模式匹配系列:

这里值得一提的是 Swift 使用 ~= 来表示模式匹配的操作符。(因为这和本文谈论的 bug 有关)
上面代码,重载了 ~= 操作符,使得 HasPrefix 支持了模式匹配。

问题的产生

(以下是我自己对这个问题的理解,有理解不对的地方欢迎大伙打脸。)

从上面 Playground 的打印我们可以看出来,代码执行了 ~=。也就是说进行了模式匹配。

可以是我们重载的 ~= 右值是个 HasPrefix 啊,switch 部分的代码是两个 String 啊, Why??

228_33120102042015

在回想上面代码对 HasPrefix 做了什么? 字面量表示,这样使我们可以简单的将 String 赋值给 HasPrefix

猜测

  1. 使用 Switch 是 Swift 会优先进行模式匹配
  2. Swift 中没有实现, 左右值都是 String~=
  3. 由于我们使用的了字面量表示,导致使用 Switch 时, String 的字面量,也可以是 HasPrefix 的字面量。
  4. 最后就执行了模式匹配

为了证实这个猜测,我尝试重载左右值都为 String~=

这回问题视乎没有了。

思考🤔

我要对 HasPrefix 进行模式匹配怎么办?

好像只能这样了。。。

我们在日常开发有可能会遇到这样的坑吗?

假设如果我们引入了一个第三方库,这个第三方库对其自定义的一个类型,实现了字面量表达模式匹配。我们好像就会遇到这个坑。

解决方法

  • 或许可以像我刚刚那个定义对应的 ~=,但我不确定会不会有其他考虑不到的问题
  • 如果你不希望进行模式匹配,你就使用 if 吧(推荐,ps:感觉这样做比较保险)

欢迎讨论、批评、指错。