一位 JavaScript 鐵桿粉眼中的 Rust!
關注「 Rust程式設計指北 」,一起學習 Rust,給未來投資
作者 | Harvard
譯者 | 彎月 責編 | 歐陽姝黎
出品 | CSDN(ID:CSDNnews)
以下為譯文:
我使用 Rust 編寫了一些小工具,而且覺得很有樂趣。我的日常工作需要大量使用 JavaScript,而 Rust 給我一種非常熟悉的感覺,因此我決定嘗試一下Rust。但與此同時,使用 Rust 完成真正有意義的工作需要重新思考程式碼的結構和合理性。編譯器是最公正無私的,然而反覆修改程式碼,直到最終通過編譯也是一種樂趣。
在這篇文章中,我將分享我在 Rust 之旅中的一些想法,以及作為 JavaScript 鐵桿粉,我對 Rust 的看法。
好訊息
現代 Rust“看起來”與現代 JavaScript 非常相似。你可以使用 let 宣告變數,而且函式看上去也很相似,由於 TypeScript 的流行,我對 Rust 的型別也不陌生,還有 async/await,總的來說,我對 Rust 有一種莫名的熟悉感。
壞訊息
問題的核心不是語法,而是 Rust 對程式內部結構的推理方式。高階語言中包含大量抽象,因此你不必擔心計算機的工作方式。這非常合情合理,如果你的目標是開車趕到辦公室,那麼只需要知道如何駕駛汽車,而不必搞懂內燃機的內部結構。相比之下,在低階語言中,你會得到螺栓和螺絲釘,為了開車去超市,你必須成為一名汽車修理工。
每種方法都有各自的優缺點,因此,它們的主要問題領域也不同。而 Rust 的目標是中間地帶。你既可以使用 Rust 訪問基礎設施,也可以使用清晰易懂的高階抽象。但是,開發人員勢必會為此付出代價:你必須學習一種新的程式推理方式。
記憶體管理
計算機程式依賴記憶體中的讀寫。記憶體讀寫是不可避免的,就像巧婦難為無米之炊。做飯的時候,我們必須將食材都買回來,然後洗乾淨切好,再按照正確的順序將它們放入鍋中,吃完飯後還需要收拾廚房裡的爛攤子。
而高階語言提供了垃圾收集器,就像我們的父母,他們會耐心地幫你打掃衛生,而你則無需弄髒雙手。然而,Rust 的記憶體管理是:“嗯……真正的廚師會清理自己的垃圾”。這也並非全無道理,因為垃圾收集器本身就有很深奧的問題,而且會帶來意料之外的結果。但與此同時,Rust 借鑑了其他語言的過往經驗,並強制程式設計師管理好記憶體。
作用域
在 Rust 中,變數只能在某個作用域內使用。如果這個作用域不再有效,則這塊記憶體就會返回給系統。編譯器會向在程式碼中注入一段程式碼來確保這一點。這在 Rust 中是鐵一樣的定律。
下面,我們來看一個示例。
這段程式碼有兩個作用域。一個外層作用域來自 main,還有一個內層作用域。Rust 的所有權如下:
-
main 擁有 a 和 b;
-
a 想要使用內層作用域,所以 main 將 a 的所有權轉移到內層;
-
內層作用域處理a,然後完成;
-
Rust的隱藏程式碼丟棄 a 的作用域;
-
main 處理 b,然後完成;
-
Rust 丟棄 b 的作用域。
請注意,所有權都會還給系統,而不是作用域的起源。a 的所有權不會返回給main 作用域。
等一等,這種做法聽起來很危險。如果遇到如下程式碼,該怎麼辦?
在這段程式碼中,main 作用域想再次使用 a,但是我們說當內層作用域結束時,Rust 已經刪除了 a。
程式執行到這裡的時候,不會崩潰嗎?
沒錯,程式會崩潰。
編譯器
Rust 編譯器會徹查一切,並評估程式是否可以安全執行。只有通過所有的檢查,它才會生成可執行檔案。
在上面這個例子中,編譯器拒絕提供二進位制檔案。
程式設計師的職責是瞭解 Rust 的法則,並遵守這些法則。包括這門語言所有的細節,所有的怪癖,所有的假設。否則,Rust 編譯器就會衝我們大吼大叫。另一方面,Rust 團隊一直在努力通過建立大量語法糖和清晰的錯誤訊息,幫助我們理解錯誤。而且,Rust 還有非常完善的文件和一個偉大的社群。
Rust的角色扮演遊戲
在 Rust 大陸中,變數是玩家。玩家必須屬於某個職業:法師、牧師、結構體。此外,每個玩家可能擁有不同的裝備。當然,你可以擁有兩個牧師,一個拿著權杖,一個拿著魔杖。
還記得上述程式碼中的dbg!()嗎?這是一個巨集,相當於 JavaScript 的 console.log。下面,我們來建立一個有型別的變數,並輸出日誌。
我們建立了一個 struct,本質上是一個型別。然後我們又建立了一個該型別的物件。最後,我們輸出該物件。
以上,Noob 型別的 player 連除錯資訊都沒有……
關鍵在於,我們手動建立的變數都是從 1 級開始的,沒有裝備。這裡需要裝備(用 Rust的術語說,就是 traits)。
我們來修改一下。
這一次可以了。唯一的不同就在於開頭的第一行。我們為 Noob 配備了 Debug 特性。現在,我們的player就有資格輸出日誌了。巨大的進步!
Rust 擁有大量的裝備,比其他語言更普遍。而且,你還可以自己設計並打造新裝備。
有些 trait 可以由編譯器為我們自動生成。而有些則需要自己實現。你想給你的法師打造一個盔甲?沒問題,當然可以,但是你必須提供實際的程式碼。
trait 在 Rust 的結構中根深蒂固。我們再來看一看上述那個報錯的例子。仔細閱讀錯誤訊息,我們會注意到,編譯器向我們解釋,必須“移動”變數的所有權,因為字串沒有實現 trait:Copy。
Copy trait 意味著你可以獲取一段記憶體,然後 memcpy 到其他地方,直接對位元組進行操作。
那麼,既然字串沒有 Copy trait,我們可不可以要求編譯器提供一個?抱歉,不行。Copy 的級別太低,字串無法安全地使用這個trait。編譯器知道這一點,所以不提供。當然,如果故事就此結束,Rust 就不會成為一門非常實用的語言了。實際上,字串有一個更明確的 trait,可以完成相同的工作:Clone。而字串也具有 Clone trait,因此我們只能用 Clone 來代替 Copy。
我們來稍微調整一下程式碼,像下面這樣:
在這段程式碼中,編譯器看到我們想在內層作用域中使用a,而且它看到我們可以使用 clone 來完成操作。所以,
-
a 的所有權歸 main;
-
a.clone 在建立後,被借用到內層作用域;
-
內層作用域執行操作,然後完成;
-
Rust 丟棄 a.clone 的作用域;
-
main 可以使用 a,因為 a 的所有權始終歸它所有。
當然,這不是唯一解決這個問題的方法,但我們可以通過這個例子初步探索一下所有權和trait。
總結
文字介紹的內容對於 Rust 學習來說,不過是冰山一角。根據我的個人經歷,Rust 的學習曲線很陡峭,但整個學習過程很有趣,而且物有所值!我會繼續努力學習下去!
原文連結:
http://blogs.harvard.edu/kapolos/rust-from-a-javascript-perspective/
宣告:本文由CSDN翻譯,轉載請註明來源。
推薦閱讀
覺得不錯,點個贊吧
掃碼關注「 Rust程式設計指北 」
- Node.js 開發者的 Rust 入門指南
- 厭倦 JavaScript,開發者用 Rust 開啟替換潮?
- 喜歡 Rust 的 5 大理由,你認可嗎?
- System76 基於 Rust 的新桌面環境
- 那些彎道超車的號主們
- Rust 與 C 的速度比較
- 你一定不能錯過的 Rust 記憶體安全指南
- Rust 會成為 JavaScript 基礎設施的未來嗎?
- Rust VS Python:為什麼越來越流行,取代 Python?
- 超乾貨!大型 Rust 專案經驗分享
- Rust 中這樣操作字串竟然是錯的
- 詳解 Rust 如何 Mock HTTP 服務
- 實戰:用 Rust 從頭實現一個 CLI 應用(2)
- Rust 專案實戰:從頭構建一個筆記命令列程式(1)
- Rust 版 Memcached 來了
- Rust 中的高階型別
- 據說 Rust 愛好者都喜歡的號主
- 我的 Rust GUI 開發之旅
- Rust 和 C 排序演算法效能對比
- Rust:什麼是非同步執行時?有哪些可用?