我并不是很熟悉 Rust, 所以我产生了一些疑问(当然也希望喜欢 Rust 的人能解答我的问题). 因为我比较熟悉的是 C++ 和 Haskell, 所以会下意识的拿它们来比较.
Rust 在很多地方都吸取了教训, 但造出来的东西虽然正确, 细节上却常有疏忽, 反而更难用,这是我很不理解的, 而且 Rust 官方似乎还没有修复它们的意愿, 甚至连饼都没画给我, 所以我对 Rust 仍然是观望态度, 打算再等 2 ~ 3 年.
先说一些特别细节的问题, 这些问题任意拿出来一个都不算什么, 但合在一起却有理由让我退出.
auto oneToFive() -> std::vector<int32_t> { return {1, 2, 3, 4, 5}; }
在这个角度来看, 二者区别是不大的, Rust 这么写
fn oneToFive() -> Vec<i32> { vec![1, 2, 3, 4, 5]; }
然而 Rust 的宏对类型推断的支持并不是很好, 而且需要复杂的支持, 因此 HashMap, BTreeMap (和衍生出的两个 Set) 是没有字面量的, LinkedList 也没有. hashmap![]
这么长的东西本来就很丑, 而且标准库还没提供, 这就是问题了.
同时也透漏出另一问题, Rust 对 C++ 的很多 “设计缺陷” 做了编译期的保护, 你无法写出所谓的错误的代码, 这也是我们会被编译期教做人的原因. 而实际上是这个限制有点太过了, 如果你限制了三成正确的写法去提高正确性, 那我没什么问题, 但如果你限制九成正确的写法去提高正确性, 那我宁愿你不正确. 在 if
while
等表达式的参数可以省略括号时, Rust 是唯一一个强制省略括号的, 诸如 Swift 的其他语言, 均可以补上括号, 类似让人感到束手束脚的细节有很多.
作为初始化列表的宏其实真的用处不大, 因为我们总是可以这么写
auto some = Xxxxxx {1, 2, 3, 4, 5}; // C++ let mut some = xxxxxx![1, 2, 3, 4, 5]; // Rust
当然过程宏我不说了, C++ 没有, 需要用一些复杂的手段来处理.
范围区间
// 理想中 match x { ...0 => Equal::LT, 0 => Equal::EQ, _ => Equal::GT, } // 实际 match x { x if x < 0 => Equal::LT, 0 => Equal::EQ, x if x > 0 => Equal::GT, _ => /* 然后编译器还总觉得你不完备 */, }
那我干嘛不写 if-else 呢?
再比如 Optional, 很多语言都有, 甚至辣鸡 C++ 标准库里都有这么一个玩意. 但我们把 Kotlin 和 Swift 拽过来, 情况就大不相同的, 在 Rust 里, ?
被用来处理 Result, 而 Maybe 类型就完全没有语法糖.
let y = x ?? 3 // 默认值 a?.b()?.c() // 可选链 a! // 强制解包
&
和 &&
还有 &&&
到底都是啥, 什么时候需要一个, 什么时候需要两个, 同时还会伴随着一些诡异的代码 *x < 0
, x < &0
. RAII 确实很重要, 但是牺牲如此之大我觉得不值得, 我随时都会遇到各种类型不匹配的问题, 如果不是编译器友善的提醒我那些东西的类型, 恐怕我还要在上边花费更多时间. 这时候我对类型推断的疑惑就出现了, 当然我不知道 Rust 实现的到底是啥理论, 没见过我也不好说太多. 但是作为一门类型如此复杂的语言, 还坚持用 let 推断, 这就是我不好理解的地方了.首先, Rust 有很多明显该推= 出来的东西根本推断不出来, 我不知道为什么, 一些东西的类型非常明显, 属于一眼就能看出来的类型, Rust 却看不懂. 当编译器看不懂的时候, C++ 是很方便的. 只要前缀写上就可以, 而
let x: Vec<i32> = /* ... */
我能说这有点画蛇添足么. 当然很多语言都用后置类型, 但它们的类型非常简单, 比如 Kotlin, Swift, 它们几乎就没有推断不出来的东西. 我在写 Swift 的时候, 从来不会因为某个变量的类型不够明晰而疑惑, 但 Rust 过于复杂, 又采用了一个极其绕的实现, 这就是让我不舒服的地方.
Rust 可以根据上下文推断类型, 展现了高超的编译器技术, 但我宁愿希望它没有. 如果一个类型的推断是从五行开外的得来的, 那我的眼睛就很难 phrase 这段代码, 而且今后删改的时候就更难处理, 因为我删除某个代码, 甚至可能影响五行开外的推断.
王垠就 diss 过 Rust, 也说过这个不够明确的问题, 但王垠完全没有好好用过这门语言, 以至于出现了不少低级错误, 但对这点的分析我倒是很赞成, 如果他多用用, 很快就会发现这东西不仅是不够明确, 而是极其混乱.
Rust 可以通过函数参数的类型来推断其他类型, 这个功能更让我不理解, 当然 Rust 官方可以生成不喜欢重载(你咋不干脆声称你不喜欢 Ad-hoc 呢?), 因为不重载的函数才有助于这种推断. 然后我想请问: 官方在函数非自动柯里化, 对函数式支持不够好的情况下, 为什么仍然拒绝实现可选参数, 命名参数和不定数量参数?
当然, 还有一些我并不太喜欢的东西, 像 "Hello, World!.to_string()
...
如果用惯了 C++ 的人, 很可能会把 C++ 当成某种黑魔法, 有可能造成一些副作用, 需要人类主动的去控制. 而人们对 Rust 的期望是, 既能起到黑魔法的效果, 又能规避大部分错误. 于是它们把咒语写的极其复杂晦涩, 说十句都施不出来正确的魔法, 每个咒语都是绕口令.
那你觉得魔法师会用哪个呢?
当感情成为生活的一种负担而且无力改变的时候。
与女生不同,大多数男生是不会把感情当作生活的全部的,过日子是头等大事,感情只是其中一部分。所以对于男生来说,一段好的感情是能让生活更轻松的。
一个男生可能会因为你漂亮而喜欢你,但这种喜欢无非是荷尔蒙冲动无法长时间维系,真正能让男生愿意和你一辈子走下去的,是你能够理解他,能够支持陪伴,能够默契相处。
但就这一点上,很多女生都是在逆行。她们所期望的感情都是建立在对于男生的索取之上,她们会因为男朋友没有足够专注自己而去作,会因为男朋友没满足自己的需要而去闹,会频繁吵架提分手来试探这个男人是否足够爱自己,却忽略了这个过程中,自己一步步在抛掉自己值得被爱的筹码。
当男生觉得和你在一起,不但体会不到丝毫乐趣,反而成为一种负担的时候,其实就已经在积累失望了。但这个时候男生一般不会直接放弃,他们会尝试逃避,会尝试沟通,会寄希望于你能逐渐去改变。
所以有经验的女生应该知道,男人的态度变化是有个周期的,不会因为你和他吵几次而一下子和你分手,但是会逐渐冷漠下来,这个过程中他就是在开始怀疑是否真的合适,会希望看到你有所变化。然而往往女生在这个阶段,又会因为男生的逃避而更加变本加厉去作去闹,想要控制对方,最终形成恶性循环。
等到失望积累够了,也就变成绝望。他会判断你已经不会变了,无药可救了,再相处下去只会让那自己越来越累,最终决定分开,等你意识到问题所在时已经来不及了,这种理性思考下的分手,往往挽回难度也是比较大的。
很多女生整天担心男生变心不爱自己,但同时却又是亲手在不断把他往外推,想想还挺可笑的。
当感情成为生活的一种负担而且无力改变的时候。
与女生不同,大多数男生是不会把感情当作生活的全部的,过日子是头等大事,感情只是其中一部分。所以对于男生来说,一段好的感情是能让生活更轻松的。
一个男生可能会因为你漂亮而喜欢你,但这种喜欢无非是荷尔蒙冲动无法长时间维系,真正能让男生愿意和你一辈子走下去的,是你能够理解他,能够支持陪伴,能够默契相处。
但就这一点上,很多女生都是在逆行。她们所期望的感情都是建立在对于男生的索取之上,她们会因为男朋友没有足够专注自己而去作,会因为男朋友没满足自己的需要而去闹,会频繁吵架提分手来试探这个男人是否足够爱自己,却忽略了这个过程中,自己一步步在抛掉自己值得被爱的筹码。
当男生觉得和你在一起,不但体会不到丝毫乐趣,反而成为一种负担的时候,其实就已经在积累失望了。但这个时候男生一般不会直接放弃,他们会尝试逃避,会尝试沟通,会寄希望于你能逐渐去改变。
所以有经验的女生应该知道,男人的态度变化是有个周期的,不会因为你和他吵几次而一下子和你分手,但是会逐渐冷漠下来,这个过程中他就是在开始怀疑是否真的合适,会希望看到你有所变化。然而往往女生在这个阶段,又会因为男生的逃避而更加变本加厉去作去闹,想要控制对方,最终形成恶性循环。
等到失望积累够了,也就变成绝望。他会判断你已经不会变了,无药可救了,再相处下去只会让那自己越来越累,最终决定分开,等你意识到问题所在时已经来不及了,这种理性思考下的分手,往往挽回难度也是比较大的。
很多女生整天担心男生变心不爱自己,但同时却又是亲手在不断把他往外推,想想还挺可笑的。