從Go遷移到Rust:為了開發一款解釋器,我都經歷了些什麼?


從Go遷移到Rust:為了開發一款解釋器,我都經歷了些什麼?

我是名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入門曲線確實陡峭,看完經典的入門圖書(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/)


分享到:


相關文章: