Swift 语言的设计错误

开发
在『编程的智慧』一文中,我表扬了 Swift 语言的 option type 设计,但这并不等于 Swift 的设计是完美没有问题的。其实 Swift 1.0 刚出来的时候,我就发现它的 array 可变性设计存在严重的错误。Swift 2.0 修正了这个问题,然而他们的修正方法却仍然是错误的。这个错误一直延续到今天。

 在『编程的智慧』一文中,我表扬了 Swift 语言的 option type 设计,但这并不等于 Swift 的设计是***没有问题的。其实 Swift 1.0 刚出来的时候,我就发现它的 array 可变性设计存在严重的错误。Swift 2.0 修正了这个问题,然而他们的修正方法却仍然是错误的。这个错误一直延续到今天。

 

Swift 1.0 试图利用 var 和 let 的区别来指定 array 成员的可变性,然而其实 var 和 let 只能指定 array reference 的可变性,而不能指定 array 成员的可变性。举个例子,Swift 1.0 试图实现这样的语义:

var shoppingList = ["Eggs", "Milk"]

shoppingList[0] = "Salad" // 可以对 array 成员赋值

let shoppingList = ["Eggs", "Milk"]

shoppingList[0] = "Salad" // 不能对 array 成员赋值,报错

然而这是错误的。为了理解这个问题,首先你应该清晰地区分 array reference 和 array 成员的区别。在这个例子里, shoppingList 是一个 array reference,而shoppingList[x] 这样的形式,是访问一个 array 成员。因为 var 和 let 本来是用于指定 shoppingList 这个 reference 是否可变,也就是决定 shoppingList 是否可以指向另一个 array。它并不能用于指定 array 成员的可变性。正确的用法应该是这样:

var shoppingList = ["Eggs", "Milk"]

shoppingList = ["Salad", "Noodles"] // 可以对 array reference 赋值

let shoppingList = ["Eggs", "Milk"]

shoppingList = ["Salad", "Noodles"] // 不能对 array reference 赋值,报错

var 和 let 只能有一个用途。它们一旦被用于指定 reference 的可变性,就不再能用于指定 array 成员的可变性。如果你理解 reference 是放在栈(stack)上的,而 Swift 1.0 的 array 是放在堆(heap)上的,就会明白 var 和 let 其实只能指定栈上数据的可变性。堆上的数据可变性,必须用另外的方式来指定。

实际上很多语言都已经看清楚了这个问题。C++ 程序员都知道 int const * 和int * const 的区别。Objective C 程序员都知道 NSArray 和 NSMutableArray 的区别。我不知道为什么 Swift 的设计者看不到这个问题。

Swift 2.0 修正了这个问题,可是它的修正方法却是错误的:它把 array 从 reference type 变成了所谓 value type,也就是说把 array 放在栈上,而不是堆上。这貌似解决了以上的问题,因为由于 array 是栈数据, shoppin List 不再是一个 reference,而是代表 array 的数据本身,所以你确实可以用 var 和 let 来决定它是否可变。

这看似一个可行的解决方案,然而它却没有击中要害,它带来了另外的问题。把 array 作为 value type,使得每一次对 array 的赋值或者参数传递,都必须进行拷贝。你没法使两个变量指向同一个 array,也就是说 array 不再能被共享。

这违反了程序员对于数组这种大型结构的“心理模型”,他们不再能清晰方便的对 array 进行思考,容易犯错误。而且对数组的拷贝需要大量时间,对效率有很大影响。没有任何另外的现代语言(Java,C#,……)把 array 作为 value type。

实际上如果你看透了它的实质,就会发现 value type 这东西的存在,其实是没有意义的。众多的新语言,Swift,Rust 试图利用 value type 之类来解决内存管理的效率问题,然而它带来的好处是微乎其微的,给程序员带来的麻烦和困扰却是有目共睹的。

从 Swift 的设计犯下这样的低级错误,你也许可以看出来,编译器专家并不等于程序语言专家。很多程序语言专家一看到 Swift 最初的 array 设计,就知道那是错的。为什么 Swift 的设计者直到 1.0 发布都没有发现,到了 2.0 修正却仍然是错误的呢?我猜这是因为 Apple 并没有聘请合格的程序语言专家进行 Swift 的设计。Swift 的***设计师是 Chris Latner,也就是 LLVM 的设计者。Chris Latner 是一个不错的编译器专家,然而他却只能算是业余级别的程序语言设计师。编译器和程序语言,真的是两个非常不同的领域,不要盲目的以为好的编译器作者就能设计出好的程序语言。

由于没有招募到深入精髓的设计师,自己瞎倒腾,使得 Swift 语言团队犯下根本不该犯的错误。***次就应该做对的事情,却需要经过很多次的返工。以至于每出一个新的版本,就出现很多“breaking change”,导致之前版本的 Swift 代码不再能用。这种情况对于 Swift 语言并不是世界末日,然而我希望 Apple 能够早点招募真正的语言设计专家,采纳他们的建议。

责任编辑:赵立京 来源: 王垠
相关推荐

2015-04-17 16:07:11

swiftOC

2014-06-04 10:52:56

Swift苹果iOS

2014-06-03 10:44:20

Swift开发语言

2015-08-14 11:37:37

Swift语言中文版

2014-08-01 15:16:05

SwiftC语言

2015-06-09 11:12:31

Swift语言Swift特性

2015-12-04 13:57:09

Swift编程设计准则

2014-06-26 10:21:04

Material DeSwift

2021-04-29 09:02:44

语言Go 处理

2016-08-19 10:41:42

Swift 2错误

2017-09-25 14:22:26

Swift4.0正式发布添加归档

2014-11-17 10:05:12

Go语言

2023-03-10 09:00:49

Swift开发者工具

2015-12-07 09:46:26

swift编程开源

2015-01-12 13:04:39

Swift开源项目汇总

2018-03-11 08:30:32

Swift 编程语言C语言

2020-10-19 09:34:04

C语言内存错误编程语言

2024-01-04 07:49:00

Go语言方法

2010-01-22 10:26:40

C++语言

2015-04-14 14:23:38

苹果Swift编程语言
点赞
收藏

51CTO技术栈公众号