最近,一篇比较 Rust 和新出的 Nim 的语言的文章出现了,作者列举了 Rust 总总繁琐啰嗦的地方,然后在 Hacker News 中提供了新的弹药。
如果要对一个浮点数数组进行排序,在 Python 中非常简单:
1 |
xs.sort() |
在 Haskell 中也非常简洁:
1 |
sort xs |
而在 Rust 中你得写这么一长串怪物:
1 |
xs.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Less)) |
是不是很坑?太恶心了吧。他将这种代码归咎于 Rust 的宗教原因,认为这是一种惩罚。是挺宗教的,把简单的事情搞复杂。
且慢,来,我们回想一下浮点数包括哪些东东:正常的数字,+0 -0 +∞ -∞,还有 NaN……
等等 NaN?NaN 的意思是“不是一个数字”。既然不是数字,也就没有办法和数字比较大小了。
稍微解释一下,xs 是一个数组,sort_by 传入一个闭包函数,通过闭包函数进行排序,先用前一个数比较后一个数 a.partial_cmp(b)
,它的返回值是一个 Option
,表示这个函数有可能运行失败,.unwrap_or(Less)
就是说当运行失败的时候,返回 Less。
在这种情况下,两个数进行比较,如果其中是一个是 NaN 就会返回 Less。
这里用我很喜欢的 Python 来做反面教材:
1 2 3 4 5 6 |
>>> sorted(map(float, ['1', '2', 'nan', '4', '3'])) [1.0, 2.0, nan, 3.0, 4.0] >>> sorted(map(float, ['1', '5', '2', 'nan', '4', '3'])) [1.0, 2.0, 3.0, 4.0, 5.0, nan] |
Oh,要死啦。
Ruby 要好得多,会抛出错误:
1 2 |
> [1.0,2.0,Float::NAN].sort ArgumentError: comparison of Float with Float failed |
(补充一下,Haskell 貌似不是 IEEE 标准的浮点数,没有 NaN。)
尽管你说或许你能保证不出现 NaN,但是无论如何对于 IEEE Float 来说,都不是良序的,所以你必须考虑到这种极端的边界情况,正是因此才能保证安全。而且不是像 Ruby 靠运行时抛出和捕获异常,而是靠编译时的类型安全。
如果这就是 Rust 的宗教,那么这一定是我喜欢的宗教了,
严谨一点好。
要是除零错也显式地处理就好了,至少弄个 lint,这个错更加常见。
Haskell 的做法似乎倾向于统一用 Maybe 来搞定,所以如果有风险就包成 Maybe Float 之类的东西,万一死了就踢出一个 Nothing 来。当然以 Haskell 的做派 IEEE float 肯定是不能用的就是了。
Safety! More safety! (Yelling
这是我喜欢rust的地方,把一切可能发生问题的地方明白的展现在眼前,而不是隐藏。