我是名C/C++程序员,早些年学过VB(不是VB.Net,暴露年龄了),做过几年Java,工作中用过Perl和Shell,在Python、Go和Lua方面刚刚入门(就是做过一两个小项目),业余常用C#(Unity)做点游戏补贴家用,也曾经上线过用Swift/Objective C做过的iPhone App,曾经试着用JavaScript/TypeScript/Node.js做了几款微信小游戏(后来搞不定游戏版号放弃了),至于Fortran、Pascal、Foxbase这些上学时学过的远古编程语言,我就不拿出来吓唬人了。
像我这样的人:学习过不少编程语言(或主动或被动,或深入或浅尝),了解点编译原理(为公司修改过JavaScript解释器,写过DSL到C++的生成器)。
应该早就看淡了编程语言之争,面对“XXX是世界上最好的编程语言”这种呼声,应该一笑而过的。为什么还要学习Rust这样一门新语言呢?
还不是看广告看的。
作为一名C/C++程序员,我曾经被内存漏洞和多线程竞争搞得焦头烂额,各种工具和编程规范也搞过不少,收效甚微。而Rust最近被很多人推崇,貌似能够解决这类问题。我也想尝试一下,赶赶时髦。借鉴当年学Go没有深入的教训,我决定动手做点东西,首先选择的是用Rust语言编写一个解释器。
做完这个看起来还算简单的项目,长吁一口气,还是有点东西可以总结的:
Rust入门曲线确实陡峭,看完经典的入门图书(https://doc.rust-lang.org/book/),感觉一头雾水。纸上得来终觉浅,绝知此事要躬行。
Rust和Go的编程思想不太一样,我在开发解释器时,已经尽可能用与原作同样的代码逻辑来实现了,但还是有些做不到,例如:
- 强制类型转换:原文使用Go语言,通篇的interface和类型强转,我在Rust里做不到。Rust没有反射,trait和struct,trait和trait之间的转换又很不方便,使用Any trait做downcast_ref,会遇到生命周期的问题。于是我上网google,发现用enum和match会更好地满足我的需求;
- 函数指针的问题:原文中使用Go语言函数指针(实际上是对象方法指针)作为解析函数,我在Rust中这么做时,发现由于是可变(mut)方法指针,编译器报出很多安全性错误。分析一下,也是合理,最终没能走通这条路。也让我感受到了Rust和Go对安全要求方面的差距。
- 空值问题:空值引用号称十亿美元的错误(Null References: The Billion Dollar Mistake),很多现代语言已经不支持空值了,例如Swift、Rust。在原文中,作者代码有时会“忘记”空值判断(根据上下文,也许并不会出问题),但使用过Rust的Option后,觉得Go中使用nil还是很不让人放心。
- 枚举类型:Rust强大的枚举和匹配,在做类型展开时,非常直观。据说这是因为Rust有函数式语言的血统。遗憾的是我并没有其它函数式语言的经验,只是觉得第一次见Rust这么用,很酷!
- if let语句:跟match语句有点类似的if let语句,也可以做类型展开,而且,没有匹配成功时,还不会触发借用,很合理,很强大。好吧,你也许不知道我在说什么,撸一遍代码就了解了。
- 哈希:原作在实现自定义语言的哈希时使用了Go语言的map,又给出了一个很奇怪的实现方式,给出的解释是Go的map不能怎么怎么着。Go语言我了解的不多,但Rust实现时可以很直观,很方便,很简单,所以我没用原作者的实现方案,迄今还没有发现我的实现有什么问题。
- Rust没有垃圾回收,但有确定性析构:原文中专门拿出一节讨论了实现解释器时对象析构的问题,说是Go的垃圾回收起了作用。搞得我很惶恐,因为我用的Rust语言没有垃圾回收,但仔细分析一下,用不到啊,Rust的确定性析构决定了,该释放就释放了,哪那么多废话。
- 引用计数:Go语言自带引用计数,可以自动垃圾回收,使用起来还是很方便的,但Rust语言使用引用计数智能指针,语法方面就比较繁琐了。
- Box:在阅读Rust入门文档时,没有理解使用Box的意义,当开发遇到循环定义时,编译器劝我还是用Box吧,我被说服了。唉!难怪说Rust是“错误驱动开发(error-driven development)”,替Rust编译器心疼,为了大家你付出的太多了!
- Rust的测试框架跟Go的测试框架有细微的差别,我没办法传递“testing.T”对象到辅助函数中,只能对测试逻辑做简单调整。
有些Rust特有的东西,还是很让人抓狂的:
- 生命周期是Rust专有概念,我还没有彻底搞懂,这个项目使用的不多。
- 引用、借用、移动、Copy、Clone、Box、Any、Sized,太烦了,很多时候我都是编译器让我怎么改,我就怎么改,懒得仔细思考!
- 模块定义和使用,我只能做到怎么简单怎么来,太复杂的设计累脑子。
还有其它方面的遗憾:
- 本项目没有涉及到多线程同步等内容,最想学的还没学到。
- 本项目没有引用外部crate,对Rust生态没有直观感受。
- 没有涉及Macro元编程这种高级货。
- 不能跟C语言接口,就不算是好的编程语言。Rust当然能接C语言,我没涉及到。
- 不安全Rust用法没有涉及。
选择一个能覆盖全部Rust语言特性的实用项目哪那么容易。
总体来说,用Rust开发还是很有特点的,经历了略显痛苦的开发磨合,对开发出来的东西更踏实了,这是C/C++语言所不能带来的踏实感!
如果大家对我编写的解释器(或图书)感兴趣,或有意见和建议,请联系我(john.hack at outlook.com),图书链接如下(http://longwaystudio.cn/waiir/)