| CSDN(ID:CSDNnews)
我從事專業的 C++ 編程工作已經四年多了,然而,三個月前我找到了一份新工作需要使用 Rust。我想通過本文,分享一下我個人關于在兩種語言之間切換的經驗和想法。
聲明:本文不打算比較 C++ 和 Rust,文中的觀點源自個人的經歷。
1 C++ 和 Rust 的工作介紹
我認為,個人從事的工作會極大地影響對語言的體驗,下面我來簡單介紹一下背景。
我使用 C++ 大多從事的是編寫數據庫的工作。數據庫不是常見的應用程序,通常它們獨占服務器,可以使用大量的系統調用,而且在有些情況下甚至可以完全繞過內核。另一方面,這意味著數據庫是研究特定語言的一個有趣的樣本,因為你需要關注性能,為客戶提供良好的用戶體驗,同時還需要保證正確。
鑒于公司的保密協議,所以我不能分享工作細節。我運行 Rust 的環境是具有高負載和高標準性能要求的數字運算異步服務器。它有點類似于數據庫,但用戶體驗可能并不那么重要。
就本文而言,我認為這兩者非常相似。
2反復談及的方面
Rust 具有良好的內存安全保證。這個話題已被反復討論,所以不在此贅述。雖然我已經使用 C++ 四年多了,但即便是已經過審查和合并的代碼,偶爾也會遇到內存問題引發的服務器崩潰。很難說有多少這樣的代碼進入了生產環境,因為人們只是在發生段錯誤時重新啟動服務器。模糊測試確實可以確保覆蓋一些奇怪的案例,但終究不是靈丹妙藥。總的來說,我認為交付 Rust 代碼比 C++ 代碼更安心。
3構建系統
在日常工作中,最令我厭煩的工作莫過于構建 C++ 代碼。作為一名開發人員,我希望能夠編寫一個簡短的命令來構建整個項目。“在構建服務器之前,你只需要運行這兩個命令……”,這句話簡直太可怕了,因為這表明構建過程是多步驟的。每次都需要運行這兩個命令?只有當這兩個文件發生變化時,需要運行這兩個命令,這句話什么意思?如果我使用 sanitizers 構建項目,這些命令會如何變化?構建過程不支持 sanitizer 是什么意思?為什么構建腳本突然開始輸出鏈接錯誤?
我說了這么多,其實就想表達一個觀點:缺乏統一的構建系統真的很令人苦惱。Bazel 是朝著正確方向邁出的一步。CMake 至今仍是我的噩夢。
在經歷了這一切后,我感覺 Rust 將我從地獄拉到了天堂。你只需編寫一個簡短的命令來構建整個 Rust 項目。更重要的是,這個世界上所有其他的 Rust 項目使用的都是同一個構建系統,因此你無需將構建腳本從他們的系統轉換到自己的系統。你只需要 Cargo.toml 中的一行代碼,就可以將構建過程所有的依賴項都包含進來。它甚至會自動將正確的編譯標志傳遞進來。
4編譯器
來自兩個編譯器的錯誤消息真的很讓人崩潰,你需要付出大量努力才能正確理解和修復。
在 C++ 中,錯誤消息的大小都是以千字節為單位衡量的。終端模擬器中的無限滾動是絕對必須的,因為 C++ 編譯器太喜歡輸出文本了。幾年后,你會形成某種直覺,只要看到錯誤多少就能判斷自己是應該閱讀錯誤還是看看代碼。通常錯誤消息越大,隨便看看代碼的作用就會大于閱讀錯誤消息。我感覺,如果 C++ 不改變模板的定義方式,這個問題永遠無解。
在 Rust 中,編譯器錯誤(在修復所有拼寫錯誤之后)通常是非常糟糕的消息。通常,這些錯誤消息表明你需要通過某種方式重新組織代碼,或花一些時間調整生命周期,所以你不可能錯誤地使用內存。雖然這個過程需要時間,而且很煩人,但正確的方法是認真聽取編譯器的建議。說起來很慚愧,通常聽取建議有助于編寫更好的代碼。此外,錯誤消息可以完整地顯示在一頁內,很方便閱讀。
5類型系統
在 Rust 的類型系統中表達想法是一種享受。
首先,非常感謝沒有鴨子類型的泛型。Traits 清楚地表達了類型希望看到的合約結構或函數。這也有助于編譯器生成有用的錯誤消息。我們獲得的錯誤消息不再是:“對第 Y 行的方法 clone 的無效引用”,而是“類型X沒有實現 Clone”,清晰明了。
其次,枚舉非常強大。Result 和 Option 是非常強大的概念,每個人都在使用。這兩個枚舉是所有庫(包括標準庫)表達容易出錯的計算和可選值的通用語言。在 C++ 中,我們有 (1) 返回錯誤代碼;(2) 返回無效值;(3) 引發異常;(4) 進程崩潰。所有有問題的選項和每個庫都使用不同的選項。除了 Result 和 Option 之外,我發現定義標記枚舉的功能也非常方便。
總結
總的來說,切換到 Rust 后,我感覺日常工作體驗得到了極大地提升。Rust 的工具非常友好,語言富有表現力且功能強大。我非常喜歡 Rust,而且我希望Rust 將來的發展能夠避免 C++ 的一些陷阱。