摘要:近年來,Rust 絕對是一門成長速度飛快的程式語言,許多國內外大廠都開始關注這門年輕的語言,但本文作者表示,對於創業公司而言,Rust 可能並不是那麼好。
連結:https://mdwdotla.medium.com/using-rust-at-a-startup-a-cautionary-tale-42ab823d9454
作者 | Matt Welsh
譯者 | 彎月 責編 | 鄭麗媛
老實說,寫這篇文章時,我有點猶豫,因為我不想捲入程式語言之戰。但是,有很多人問我關於使用 Rust 的體驗,以及是否應該在項目中選擇 Rust。因此,我想通過本文分享一些在創業環境中使用 Rust 的利弊。
我認為,從某些方面來看,Rust 確實很優秀。本文並不是要批評 Rust,我只是想說明 Rust 可能會對生產力造成重大影響,尤其是對於創業公司而言,速度是一個非常重要的考慮因素。你需要仔細權衡對公司和產品來說,選用這門程式語言帶來的優勢與其對產品開發速度的影響是否值得。
首先,我必須說明,Rust 非常出色地實現了其設計目標,如果你的項目需要 Rust 的特定優勢(高性能、超強類型、不需要垃圾收集的系統語言),那麼這將是一個不錯的選擇。但我認為,很多時候人們會在並不合適的情況下選用 Rust,結果導致團隊因 Rust 的複雜性而付出沉重的代價,沒有收穫太大好處。
我有大約兩年的 Rust 使用體驗,主要來自於之前的一家創業公司。那是一個基於雲的 SaaS 項目,產品本身基本算是一個傳統的 CRUD 應用,由一組微服務構成,位於資料庫前面,提供了 REST 和 gRPC API 端點,還有一些後臺的微服務(實現結合了 Rust 和 Python)。我們選用 Rust 的主要原因是,公司的幾位創始人都是 Rust 專家。隨著時間的推移,我們的團隊規模不斷擴大,工程人員數增加了近 10 倍,程式碼庫的規模和複雜性也大幅增加。
隨著團隊和程式碼庫的增長,我感覺到我們為使用 Rust 付出的代價越來越沉重。開發的速度越來越遲緩,推出新功能所花費的時間也超過了預期,而且我們團隊也感受到了選用 Rust 對生產力帶來的負面影響。從長遠來看,用另一種語言重寫程式碼會提高開發的靈活性,並加快交付速度,但是騰出時間來重寫核心程式碼的難度非常高。所以,我們陷入了僵局,要麼繼續忍受 Rust,要麼硬著頭皮重寫大量程式碼。
可能你會問,Rust 應該是一門非常出色的程式語言,但為什麼我們的使用感受並不好呢?


Rust 的學習曲線非常陡峭
縱觀整個職業生涯,我使用過幾十種語言,除了少數語言之外,大多數現代過程式語言(C++、Go、Python、Java 等)的基本概念都非常相似。
每種語言都有自己的特別之處,但通常你只需要學習不同語言的關鍵模式,就能迅速掌握並快速編寫程式碼。然而,對於 Rust,我們需要學習許多全新的概念,比如生命週期、所有權和借用檢查器等。對於大多數使用其他通用語言的人來說,這些概念都很陌生,即便是對於經驗豐富的程式設計師來說,Rust 的學習曲線也相當陡峭。
當然,這些「新」概念原本就存在於其他語言中,尤其是函數式語言,但 Rust 將它們帶入了「主流」語言環境,因此對於許多 Rust 新手來說很陌生。
儘管我的同事都是聰明且富有經驗的開發人員,但團隊中的許多人(包括我自己)在理解 Rust 的規範時都遇到了麻煩,比如編譯器神秘的錯誤訊息,以及如何掌握核心庫。我們舉辦了每週例會,集體學習 Rust,以幫助大家分享知識和專業技術。隨著開發速度的減慢,團隊的生產力和士氣都很低迷。
與之相比,我在 Google 工作時,團隊採用的程式語言從 C++ 轉變為 Go 只用了不到兩週的時間。可這一次我們掙扎了數月,團隊中的大多數人依然無法完全掌握 Rust。許多開發人員告訴我,他們經常感到很窘迫,因為開發新功能所需的時間超出了預期,而且他們需要花費大量時間思考如何使用 Rust。

我們可以通過其他方法解決 Rust 試圖解決的問題
如前所述,我們構建的服務是一個相當簡單的 CRUD 應用,而且服務的負載只不過是每秒幾個查詢。服務背後是一個非常複雜的資料處理管道,需要數小時才能運行完成,因此服務本身不太可能成為性能瓶頸。我們不需要擔心 Python 之類的傳統語言會遇到的性能方面的問題。除了所有面向 Web 的服務都需要完成的處理之外,沒有特殊的安全或併發要求。
我們使用 Rust 只有一個原因——因為最初編寫該系統的開發人員是 Rust 專家,並不是因為 Rust 特別適合構建這種服務。
相較於開發人員的生產力,Rust 更重視安全性。在許多情況下,這種權衡並沒有問題,比如構建 OS 核心程式碼,或者記憶體十分有限的嵌入式系統。但我認為並非在所有情況下這種權衡都是正確的,尤其是十分重視開發速度的創業公司。
我是一個實用主義者,我寧願讓團隊花時間調試用 Python 或 Go 編寫的程式碼偶爾出現的記憶體洩漏或類型錯誤,也不願讓團隊中的每個人為了使用這種語言而導致生產力大幅下降。
如上所述,我在 Google 工作時,我們團隊用 Go 構建了一項服務。過了一段時間後,該服務發展到為 8 億使用者提供支持,而且高峰期的 QPS(每秒請求數)是Google搜尋的 4 倍。在構建和運行這個服務的那些年裡,由 Go 的類型系統或垃圾收集器引發的問題,我用一隻手就能數過來。
所以基本上,Rust 試圖解決的問題都可以通過其他方式解決,比如良好的測試、linting、程式碼審查以及監控等。當然,並不是所有的軟體項目都有這種奢侈的配置,所以我可以想象,在有些情況下 Rust 確實是一個不錯的選擇。

Rust 開發人員的招聘難度很大
在這家創業公司工作期間,我們招聘到了很多人,但在加入工程團隊的 60 多人中,只有兩三個人有 Rust 的使用經驗——不是因為我們不想招 Rust 開發人員,而是招不到。近來,隨著 Rust 的流行度增加,Rust 開發人員數量也有所增加,但在採用 Rust 時,你必須有心理準備:Rust 開發人員的招聘難度很大。
另一個你需要考慮的因素是,使用 Rust 時,團隊內懂 Rust 的人和不懂 Rust 的人會分裂成兩個陣營。由於我們選用了這種「深奧」的程式語言,公司內本來可以幫忙構建功能、調試生產問題的工程師也幫不上忙了,因為他們完全搞不清楚 Rust 程式碼。
當你需要快速開發,並將團隊中每個人的力量都凝聚起來時,工程團隊缺乏替補隊員將成為一種巨大的劣勢。根據我的經驗,通常開發人員在 C++ 和 Python 等語言之間切換時沒有什麼困難,但 Rust 是一門新技術,而且非常複雜,因此會阻礙團隊成員之間的相互合作。

庫和文件不成熟
隨著時間的推移,相信這個問題會得到解決,但與 Go 語言相比,Rust 的庫和文件生態系統非常不成熟。
Go 語言的優勢在於,這門語言由 Google 的整個專業團隊開發和支持,因此文件和庫都非常完善。相比之下,Rust 的庫和文件尚在長期的開發中。許多流行庫的文件非常罕見,人們常常需要閱讀源程式碼才能了解如何使用。
團隊中擁護 Rust 的開發人員經常說「async/await是一個新概念」,或者「這個庫的文件很匱乏」之類的話,這些缺點對團隊的影響非常大。比如,我們採用了 Actix 作為服務的 Web 框架,這是項目早期犯下了一個巨大的錯誤,這個決定為我們帶來了巨大的痛苦,我們遇到了很多深埋於庫中的錯誤和問題,但沒有人知道如何修復。
當然,這種不成熟並不是 Rust 特有的,但這是選擇 Rust 語言必須付出的代價。無論核心語言文件和教程多麼出色,如果不知道如何使用這些庫,一切都於事無補。

使用 Rust 編寫新功能非常困難
不知道其他人如何,但至少對我來說,在構建一個新功能時,一般我並不會事先準備好所有的資料類型、API 以及其他細節。通常,我會邊想邊寫程式碼,實現一些基本的想法,然後檢查我的假設是否正確。
用這種方式編寫 Python 程式碼沒有任何問題,因為你可以快速編寫一些程式碼,諸如類型等要求,不需要擔心特定的邊緣情況會導致程式碼出錯。你可以回頭再整理程式碼,並修復所有類型錯誤,編寫所有測試。
但是,你很難使用 Rust 編寫出這種類似於「草稿」的程式碼,因為編譯器會抱怨每一個沒有通過類型以及生命週期檢查的錯誤。如果是構建需要放入生產環境的程式碼,這種檢查非常有意義,但我只是在測試一些想法,這些錯誤會打斷我的思路。宏 unimplemented! 有一點幫助,但依然要求你確保所有的類型正確,不然連編譯都無法通過。
當需要修改承載接口的類型簽名時,雖然你只是想驗證一下自己的想法是否可行,卻發現需要花費數小時修改每一處使用該類型的程式碼,而且每一次修改,都需要檢查一遍,真的很令人崩潰。

Rust 的優勢
當然,Rust 也有一些我非常喜歡的優點,有一些特性我非常希望其他語言也借鑑一下。首先,match 很棒。其次,Option、Result 和 Error 特性非常強大。另外,運算子 ? 是一種優雅的處理錯誤的方式。可能其他語言中也有類似的特性,但 Rust 的處理尤為優雅。
如果是高度重視性能和安全性的項目,我絕對會選用 Rust。對於個人項目,或非常小的(比如 2~3 人)團隊,Rust 也是一個很好的選擇。最後,對於核心模組、韌體、遊戲引擎等項目來說,Rust 絕對是不二之選。
☞騰訊文件企業版正式發佈;曝蘋果 AR/VR 頭顯將運行 xrOS 作業系統;Chrome 108 正式版發佈|極客頭條
☞Hinton、DALL-E 2 皆上榜,盤點 AI 圖像 10 年合成史!
☞這 11 種程式語言,還「活著」嗎?