- 文档
【免费下载链接】swift-evolution
This maintains proposals for changes and user-visible enhancements to the Swift Programming Language.
本篇技术指南围绕 swift-evolution 仓库中由语言指导组(LSG)维护的「常见被否决提案」清单 commonly_proposed.md 展开,系统梳理 Swift 对缩进语法、分号、逻辑运算符、guard、强制解包、GC 等高频提案的否决理由,并结合仓库内的正式提案(如 SE-0004、SE-0054、SE-0095)印证这些决策背后的设计哲学。读完本文,你将理解 Swift 在"C 家族传统"、"显式优于隐式"、"工具链可解析性"三条主线上的取舍逻辑,并掌握在 Swift 论坛上发起新提案前应遵守的讨论纪律。
这份清单是什么:commonly_proposed.md 的定位
commonly_proposed.md是 Swift Evolution 流程中的一份负向设计文档——它不描述"要做什么",而是记录"为什么不做什么"。它由语言指导组(Language Steering Group)维护,这一点在 process.md 中有明确交代:
The LSG maintains a list of commonly rejected proposals.
它存在的意义非常实际:Swift 语言持续演进十几年,社区里许多想法被反复提出,而其中相当一部分与 Swift 的核心设计方向相悖、几乎不可能被接受。把这些讨论沉淀为清单,可以让后来者避免重复踩坑,也让"否决"本身成为可引用的公开决策记录。
文档开头明确了两条使用规则:
- 若想重启某个话题,必须为讨论带来新信息,而不是简单地说"我真的很想要这个"或"别的语言里有这个功能而且我喜欢它"。
- 超出范围(out-of-scope)的提案不会进入评审排期。当前仓库 README.md 列出了每个大版本发布的核心关注领域(focus areas),经正式评审后被否决的变更则收录在 Swift.org 的 Swift Evolution 仪表盘中。
换句话说,这份清单与 process.md 描述的两阶段流程(先在论坛 pitch 讨论、再进入 open review)是衔接的:清单承担"前置过滤"职能,帮提案者把精力集中在真正有希望的方向上。
理解一切否决的前提:Swift 的 C 家族血统
清单中有大量讨论引用"C family"(C 家族)语言。文档对它的定义是:在语法层面与 C 相似的语言家族,包括 C++、C#、Objective-C、Java 和 JavaScript。这是一把理解全文的钥匙——
Swift embraces its C heritage. Where it deviates from other languages in the family, it does so because the feature was thought actively harmful (such as the pre/post-increment
++) or to reduce needless clutter (such as;or parentheses inifstatements).
这句话给出了 Swift 偏离 C 传统的仅有的两类正当理由:
- 该特性被认为主动有害(actively harmful),例如
++/--自增自减运算符; - 为减少无谓杂乱(needless clutter),例如行尾分号、
if语句的括号。
反之,如果某个提议只是"换个叫法"或"别的语言这么干",却没有达到上述标准,就会被归入"非目标"(non-goal)。
仓库中 SE-0004《Remove the ++ and -- operators》 就是"主动有害"类偏离的完整案例(状态:Implemented (Swift 3.0))。该提案列举了移除++/--的七条理由,其中与上述哲学直接相关的包括:
x++相比x += 1表达优势极小;- Swift 中
=、+=等赋值类运算本就返回Void,++/--返回值的模型与之不一致; for-in、区间、map等特性消除了 C 风格for循环中使用++i的大部分场景;- 依赖返回值(如
foo(++a, a++))的代码即使语义被定义清楚,也极难阅读维护。
最终它通过"如果我们今天没有这个运算符,还会在 Swift 3 里加上它吗?"这一试金石,得出否定结论。对照阅读 commonly_proposed.md 与 SE-0004,可以完整看到"从提出到实现再到沉淀为常识"的决策闭环。
基本语法与运算符:四个被否决的方向
用 Python 风格缩进替代{}花括号
这是最能引发情绪争议的话题之一,但结论明确:Swift 不会改用缩进界定作用域。花括号是 C 家族的基本语法结构,弃用它将使 Swift 脱离其血统根基,并动摇所有已有代码与工具链。
移除;分号
文档给出了一个精细的二分处理:
- 行内分号是刻意的表达特性(一行写多条语句),应当保留;
- 行尾分号属于代码风格问题,应该交给linter处理,而不是让编译器去管。
这体现了 Swift 的一条原则:编译器负责语言语义,风格治理交给工具层。
用 "and"、"or"、"not" 替换&&、||、!
这是清单中最具技术深度的一条否决,其理由与编译器与工具链的架构直接相关:
- Swift 刻意将运算符语法与标识符语法分区(partitioned)。这是支持用户自定义重载运算符的关键前提。
- 若运算符由普通标识符(如
and)构成,编译器解析单个文件时就必须先查看其import的模块、读取"运算符声明"才能决定如何分词——这会破坏"无需解析全部 import 即可解析单个 Swift 文件"的能力,对 IDE、补全、重构等工具链是重大打击。 - 即便不考虑中缀函数,
not要像!一样省略括号,也必须获得运算符或关键字地位;而not somePredicate()在视觉上的绑定也比!somePredicate()松散得多。
有趣的是,仓库中 SE-0001《Allow (most) keywords as argument labels》(状态:Implemented (Swift 2.2))恰好展示了 Swift 如何在"关键字"与"普通标识符"之间划出精细边界:除inout、var、let三个会改变参数语义的关键字外,其余关键字都可以作为参数标签(如indexOf(value, in: collection)),因为"关键字后跟:"在语法上无歧义。"分区但精细、歧义即拒绝"正是 Swift 语法设计的统一思路。
替换?:三元运算符
?:确实"魔法感"十足,但它服务于一个重要的使用场景:在表达式中紧凑地选取两个值之一。社区对替代方案做过密集讨论,但没有一个"足够好"到值得背离 C 家族先例。否决理由是务实的:先例本身就是价值,除非替代方案显著更优。
集合类型:为什么Array下标访问不返回 Optional
"让Array<T>的下标访问返回T?或T!而不是T"是一个反复出现的诉求,但同样被明确否决,理由有两条:
- 语义层面:越界访问数组是一个逻辑错误(logic error),当前行为如实反映了这一点——你不应该"吞掉"一个逻辑错误并默默得到
nil。 - 性能层面:若下标访问需要返回 Optional,每次访问都要做边界检查并包装结果,会将数组访问拖慢到不可接受的程度。数组是 Swift 性能模型的基石,这个代价无法接受。
值得注意文档的措辞:"Changing the unlabeled array subscript to return an optional has come up multiple times before and isvery unlikely to be accepted."——这是清单中罕见的"几乎不可能"级判断,说明这条边界相当稳固。
控制流、闭包、可选绑定与错误处理:七条否决的深层逻辑
替换continue关键字
有提议用其他脚本语言的同义词(next、skip、advance)替换continue。否决理由直指设计目标:Swift 刻意让自己感觉是 C 家族的一员,在没有强烈动机的情况下把关键字换成非 C 先例的说法,是明确的 non-goal。
移除default:只用case _:
default被广泛使用、在众多 C 家族语言中有充分先例,而case _过于"魔法"。先例、可读性、惯用性三方面都不支持这次变更。
把guard改名为unless
这是清单中论证篇幅最长的条目之一,核心论点是:请求改名源于对guard语义的根本误解。
- 误解方认为:
guard只是逻辑取反的if,所以unless更直观; - 事实是:
guard强制要求其花括号内的代码为当前执行路径提供提前退出——块内必须出现return、throw、break、continue,或调用不返回的函数(如fatalError()); - 这与
if有本质区别,因此"guard与if平行"的假设不成立,改名理由也随之瓦解。
推断省略guard主体时的return
有多个提案希望允许省略guard主体以追求简洁(即只写条件不写退出语句)。否决依据是 Swift 的一条核心原则——让控制流显式且可见:
- 举例来说,
try关键字存在的唯一目的就是向人类读者指出哪里可能抛出错误; - 隐式返回会违反这一原则,用简洁换取清晰,这不是 Swift 的风格;
- 此外,退出作用域的方式远不止
return(循环里可能想break或continue),而且并非每个函数都有显而易见的默认返回值可退。
更改闭包字面量语法
闭包语法在 Swift 内部经历过仔细的论证,设计的各个方面都有强动机。任何修改提案都应对 Swift 语法有非常细致的理解,即便如此,找到更好方案的可能性也很低。
在if let中使用模式匹配取代可选解包
这一条信息量极大:Swift 团队实际上尝试过(将if let改为模式匹配形式),但收到了大量负面反馈。文档列出了五点原因,堪称一份"语言演进试错记录":
- 大多数开发者不会用"模式匹配"的术语思考问题,他们想的是"解构"(destructuring);
if let最常用的场景就是可选匹配,改动让常见场景变得更别扭;- 改动提高了 Swift 的学习曲线——模式匹配从"可以晚点学"变成"必须一开始就面对";
- 当前
if case的设计已经围绕case关键字统一了"模式匹配"概念; - 不熟悉
if case的开发者遇到它时,能顺利在搜索引擎或 Stack Overflow 上检索到答案。
第五点尤其能体现 Swift 团队的务实:语言特性不仅要好用,还要"可被搜索、可被解释"。
移除或弃用强制解包运算符!
对"移除/弃用强制解包(force-unwrap)与try!"的呼声同样被明确拒绝:强制解包是语言中合法且有价值的组成部分,并非仅仅出于源码稳定性考虑。核心团队明确表示,即使以编译器 flag 开关的模式启用,也不会考虑这类提案。文档同时留下一个开放话题:编译器是否应该获得更通用的"lint"能力来引导代码风格——风格问题归风格工具,语言语义归编译器,这与分号条目的态度一脉相承。
仓库中的 SE-0054《Abolish ImplicitlyUnwrappedOptional type》(状态:Implemented (Swift 4.2))可以看作这条决策的正面佐证:Swift 虽然收紧了隐式解包可选(IUO)的使用范围——把T!从类型降格为声明上的属性(@_autounwrapped),禁止嵌套 IUO(如[Int!])、禁止typealias X = Int!——但显式的强制解包运算符!与try!始终是合法的一等公民。正如该提案所述,IUO 是"过渡性技术",而显式!是开发者明确表达意图的工具。两条决策合起来勾勒出 Swift 的完整态度:显式解包可以,隐式传播不行。
用 C++ 风格语法替换do/try/repeat
Swift 的错误处理是刻意设计的:让代码维护者一眼看出哪些调用可能抛错。它与其他语言的异常处理在部分语法上相似,但在关键点上刻意不同——这是偏向错误使用者一方的精心平衡。文档明确指引:在提出修改前,必须通读官方文档《Error Handling Rationale and Proposal》全文,理解现状设计的原因,并准备好解释"你的改动为何值得打破这种平衡"。
杂项:两条架构级否决
用垃圾回收(GC)替代自动引用计数(ARC)
这是清单中最具系统级色彩的一条。文档承认 GC 的优势——标记-清扫式 GC 在 Java、JavaScript 等流行语言中被广泛使用,且能自动回收 ARC 需要程序员自行推理的引用环。但否决理由分量更重:
- 系统编程领域适配性:实时系统(视频/音频处理)、深度嵌入式控制器、大多数内核都普遍认为 GC 不适用;
- 内存开销:GC 只有在比进程实时使用量多给 3–4 倍内存时才高效,这个代价对 Swift 不可接受。
也就是说,GC 会把 Swift 挡在大量系统编程领域门外——而"能写系统软件"正是 Swift 的立身之本。
类型约束中的析取(逻辑或)
不允许(Int | String)这类匿名联合类型、以及在类型约束中使用逻辑或。清单引用了 SE-0095 评审期间(return for revision)的论坛讨论,直接给出结论:"这种类型的约束是类型系统无法也不应支持的。"
仓库中的 SE-0095《Replace protocol<P1,P2> syntax with P1 & P2 syntax》(状态:Implemented (Swift 3.0))提供了完整的背景:Swift 用&表达协议组合(A & B & C是"同时满足所有协议"的合取类型),Any从 typealias 升格为关键字。合取(&)被精心实现,而析取(|)被明确否决——这体现了 Swift 类型系统对"可表示性"的审慎:并非所有听起来对称的语法都有对等的类型论支撑。
给提案者的行动指南
基于这份清单与 process.md 描述的完整流程,想在 Swift 社区推进一个想法,正确的姿势是:
- 先搜索:在 Swift 论坛检索是否已有相关讨论,并对照本清单确认想法是否已被否决。若已被否决,除非你有新的技术论据,否则重启讨论没有意义。
- 先 pitch 再写提案:在论坛的 Evolution > Pitches 板块发起非正式讨论,链接到所有相关旧线程,充分吸收反馈(process.md 对 pitch 阶段有详细要求)。
- 遵循模板撰写正式提案:语言与标准库提案使用 proposal-templates/0000-swift-template.md,并以
SE-前缀提交到仓库的 proposals/ 目录;SwiftPM 与 Swift Testing 提案分别使用对应的 0000-swiftpm-template.md 与 0000-swift-testing-template.md。 - 对标当前版本焦点:参考 README.md 中列出的每个大版本焦点领域,判断想法是否契合当下发布节奏;不契合的提案可能被要求推迟到后续版本。
- 理解评审不是投票:open review 的反馈以论据质量取胜,而不是人数或嗓门;演化工作组依据"什么对 Swift 项目最有利"做判断(process.md)。
结语:一份负向文档的价值
commonly_proposed.md表面上记录的是"拒绝",实质上记录的是一套长期稳定的设计价值观:尊重 C 家族先例、显式优于隐式、保持单文件可解析性、为系统编程保留性能与确定性、风格问题交给工具而非编译器。当你理解了这些否决背后的理由,也就理解了 Swift 为什么会是今天的样子——以及什么样的提案才配得上一次正式的 open review。下次在论坛上看到"为什么 Swift 不用缩进/不要分号/改成 GC"的讨论时,你可以直接引用这份清单,把话题推进到"新论据"层面,而不是停留在"我想要这个"。
- 文档
【免费下载链接】swift-evolution
This maintains proposals for changes and user-visible enhancements to the Swift Programming Language.
相关推荐
Swift Evolution 提案解读:SE-0028 现代化 Swift 调试标识符(__FILE__ → file)及其后续演进
Swift Evolution 提案解读:SE 0028 现代化 Swift 调试标识符(__FILE__ → file)及其后续演进 导读 SE 0028《M
文档Arnis 完整指南:三步把真实城市生成我的世界方块世界
Arnis 完整指南:三步把真实城市生成我的世界方块世界 想把自家城市原样搬进《我的世界》?不必一块方块一块方块地摆。Arnis 读取 OpenStreetMa
桌面应用游戏开发GISSwift 参数默认值调用顺序强制化:解读 Swift Evolution SE-0060 提案
Swift 参数默认值调用顺序强制化:解读 Swift Evolution SE 0060 提案 本篇技术指南以 Swift 语言演进仓库(swift evol
文档
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考