昨天 Chris Eidhof 大神在 Tweets 上发了一段这样的代码,他认为这是 Swift 的一个 bug 。推文
最近在看喵神的《Swifter - Swift 必备 Tips(第三版)》,刚好看到字面量表达和模式匹配。所以也想尝试解释这个 bug 的产生原因。
字面量表达
什么是字面量?在 Swift 中我们会像下面那样赋值变量,这里的 3、Hello、true 就是字面量。
let aNumber = 3
let aString = "Hello"
let aBool = true
在上面的代码中 HasPrefix
这个 struct
继承了 ExpressibleByStringLiteral
这个协议,使得它支持了 字面量表达
。
也就是说现在 HasPrefix
可以像下面这样赋值了:
模式匹配
关于模式匹配的使用我这里就不做解释了,打开可以看 SwiftGG 上的模式匹配系列:
- 模式匹配第一弹: switch, enums & where 子句
- 模式匹配第二弹:元组,range 和类型
- 模式匹配第三弹: 自定义的模式匹配和语法糖
- 模式匹配第四弹:if case,guard case,for case
这里值得一提的是 Swift 使用 ~=
来表示模式匹配的操作符。(因为这和本文谈论的 bug 有关)
上面代码,重载了 ~=
操作符,使得 HasPrefix
支持了模式匹配。
问题的产生
(以下是我自己对这个问题的理解,有理解不对的地方欢迎大伙打脸。)
从上面 Playground 的打印我们可以看出来,代码执行了 ~=
。也就是说进行了模式匹配。
可以是我们重载的 ~=
右值是个 HasPrefix
啊,switch 部分的代码是两个 String
啊, Why??
在回想上面代码对 HasPrefix
做了什么? 字面量表示,这样使我们可以简单的将 String
赋值给 HasPrefix
。
猜测
- 使用 Switch 是 Swift 会优先进行模式匹配
- Swift 中没有实现, 左右值都是
String
的~=
。 - 由于我们使用的了
字面量表示
,导致使用 Switch 时,String
的字面量,也可以是HasPrefix
的字面量。 - 最后就执行了模式匹配
为了证实这个猜测,我尝试重载左右值都为 String
的 ~=
。
这回问题视乎没有了。
思考🤔
我要对 HasPrefix 进行模式匹配怎么办?
![Uploading 14771926936502_476997.jpg . . .]
好像只能这样了。。。
我们在日常开发有可能会遇到这样的坑吗?
假设如果我们引入了一个第三方库,这个第三方库对其定义的一个类型,实现了字面量表达和模式匹配。我们好像就会遇到这个坑。
解决方法
- 或许可以像我刚刚那个定义对应的
~=
,但我不确定会不会有其他考虑不到的问题 - 你就使用
if
吧(推荐,ps:感觉这样做比较保险)
欢迎讨论、批评、指错。