- 原文地址:Rust 2018 is here… but what is it?
- 原文做者:Lin Clark, The Rust Team
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:子非
- 校對者:iceytea
本文是與 Rust 團隊(指文中「咱們」)合著。你也可在 Rust 博客上閱讀他們的聲明html
今天,Rust 2018 發佈了它的第一個版本,在這個版本中,咱們專一於提升生產力,讓 Rust 開發人員儘量高效。前端
但除了這些,很難準確解釋 Rust 2018 是什麼。android
一些人認爲它是一門語言的新版本,確實能夠這麼認爲,但其實又不徹底是。我說「不徹底是」是由於若是這是語言新版本,它並不像其它語言的版本更新。ios
在大多數其它語言中,當新版本到來時任何新功能都會添加到此版本中。以前版本並不會獲得功能更新。git
Rust 的版本則不一樣。這是由於語言的演進方式不一樣。幾乎全部新功能都會 100% 兼容原有的 Rust。他們不須要重大的更新。這意味着不須要把他們限制在 Rust 2018 代碼中。新版本的編譯器會繼續支持「Rust 2015 模式」,也就是你默認使用的模式。程序員
然而由於有時候要改進語言,你必須添加新的東西(像新語法)。而且這類新語法會在老的代碼庫下執行失敗。github
拿 async/await
舉例來講。Rust 最初並無 async
和 await
的概念。可是事實證實這些簡單的語法實際上很是有用。它們讓異步代碼易於編寫而且不會使代碼變得笨重。web
爲了讓添加這個功能成爲可能,咱們須要把 async
和 await
添加爲關鍵字。但咱們必須當心不能使舊代碼失效……代碼可能會把 async
或 await
當作變量名使用。shell
因此咱們把添加關鍵字做爲 Rust 2018 的一部分。雖然這個功能自己還沒實現,但如今關鍵字已經被保留。接下來三年開發(例如添加關鍵字)所需的全部重大變化都是在 Rust 1.3.1 中一次性完成的。npm
雖然 Rust 2018 有重大的變化,但這並不意味着你的代碼會執行失敗。你的代碼會繼續編譯甚至會把 async
或 await
當作變量名。除非你告訴它,否則編譯器會假設你想讓它當前以相同的方式來編譯代碼。
不過一旦你想使用這些新的重大功能,你能夠設置成 Rust 2018 模式。你僅須要執行 cargo fix
,它會告訴你若是你須要更新你的代碼來使用新功能。它幾乎能夠自動地處理更改。而後你能夠添加 edition=2018
到你的 Cargo.toml 來設置和使用新功能。
Cargo.toml 中的 edition 說明符不適用於你的整個項目……它不適用於你的依賴。它只限於一個包。這意味着你將可以擁有相互依賴的 Rust 2015 和 Rust 2018 包。
所以,即便 Rust 2018 已經出現,它大體看起來與 Rust 2015 相同。大多數變化都將同時出如今 Rust 2018 和 Rust 2015 中。只有少數須要重大變化的功能不會同步支持。
Rust 2018 不只僅是對核心語言的改變。事實上,遠非如此。
Rust 2018 致力於提升 Rust 開發人員的工做效率。許多生產力的提升來自核心語言之外的東西……好比工具。他們還專一於特定用例,並弄清楚 Rust 如何成爲這些用例最有效的語言。
所以,你能夠將 Rust 2018 視爲 Cargo.toml 中的說明符,你可使用它來啓用少數須要重大更改的功能……
或者你能夠把它想象成一個時刻,在許多狀況下,Rust 成爲你可使用的最有效的語言之一 —— 每當你須要性能,輕巧的實現或高可靠性時。
在咱們看來,這是第二點。讓咱們先來看看核心語言以外的全部事情。而後咱們能夠深刻研究核心語言自己。
抽象描述的編程語言自身並不能高效,但在一些用例中它會頗有效。所以,團隊知道咱們不只須要將 Rust 做爲一種語言或 Rust 工具更好,咱們還須要在特定領域中使 Rust 更容易使用。
在某些狀況下,這意味着要爲一個全新的生態系統建立一套全新的工具。
在其餘狀況下,它意味着打磨已經存在於生態系統中的內容並將文檔作好,以便保持生態系統的成長和正常運做。
Rust 團隊成立了專一於四個領域的工做組:
對於 WebAssembly,工做組須要建立一整套新工具。
就在去年,WebAssembly 讓編譯像 Rust 這樣的語言在 Web 上運行成爲可能。從那時起,Rust 迅速成爲與現有 Web 應用程序集成的最佳語言。
Rust 很是適合 Web 開發,緣由有兩個:
使用 web-sys
和 js-sys
crates,很容易在 Rust 代碼調用相似於 fetch
或 appendChild
的 Web API 。而且 wasm-bindgen
能夠輕鬆支持 WebAssembly 原生不支持的高級數據類型。
一旦編寫了 Rust WebAssembly 模塊,就可使用各類工具將其嵌入到 Web 應用程序的其他部分中。你可使用 wasm-pack 自動運行這些工具,並根據須要將新模塊推送到 npm。
Check out the Rust and WebAssembly book to try it yourself. 建議你查看 Rust 嵌入式書籍並親自嘗試一下。
如今 Rust 2018 已經完成,工做組正在肯定下一步要作什麼。他們將與社區合做,肯定下一個重點領域。
對於嵌入式的開發,工做組須要使現存的功能穩定。
理論上,Rust 對於嵌入式開發來講已是很是出色的語言了。它爲嵌入式開發人員提供了他們很是缺少的現代工具,以及很是方便的高級語言功能。而且不須要耗費資源。所以,Rust 彷佛很是適合嵌入式開發。
然而,實際上它並很差使用。必備的功能處在不穩定階段。另外,爲了在嵌入式設備上使用,須要對標準庫作一些改變。這意味着人們必須編譯他們本身版本的 Rust 核心包(用來給每一個 Rust 應用提供 Rust 的基本構建模塊 —— 內部模塊和原始值)
總之,這兩件事意味着開發人員必須依賴於 Rust 的每日構建版本。而且因爲沒有針對微控制器的自動化測試,每日構建版在這些目標上會常常出錯。
爲了解決這個問題,工做組須要保證穩定版本有那些必要的功能。咱們也必須向持續集成系統爲微控制器目標添加測試。這意味着向桌面端組件添加功能不會破壞嵌入式組件的東西。
有了這些變化,Rust 的嵌入式開發將從有風險的前沿邁向高效。
建議你查看 Rust 嵌入式書籍並親自嘗試一下。
隨着近年來的推動,Rust 已經很好地支持了 ARM Cortex-M 架構的芯片處理器核心,這些核心用於大量設備。然而,還有不少被用於嵌入式設備的架構沒有獲得很好的支持,而且沒有被很好的支持。Rust 須要被擴展來給這些架構以一樣水平的支持。
對於網絡來講,工做組須要構建核心抽象概念到語言中 —— async/await
。這樣,開發者能夠在異步代碼中使用符合語言習慣的 Rust。
對於網絡任務來講,你必需要等待。例如,你可能須要等待請求的響應。若是你的代碼是同步的,那意味着 CPU 核心正在執行的任務會中止並且不能會作其它的任何事情,直到請求進來。但若是你的代碼是異步的,那麼等待響應的函數會在 CPU 核心執行其它函數時掛起。
使用 Rust 2015 使異步編程成爲可能。而且還有不少優勢。從大的方面講,像服務端應用之類的服務,你的異步代碼使得每一個服務器能夠處理更多的鏈接。從小的方面講,對於那些運行在小型單核 CPU 上的嵌入式應用,異步使你更好的利用單線程。
但這些優勢也帶來了一個大的缺點 —— 你不能爲那些代碼使用借用檢查器,而且你將必須書寫符合語法習慣的(和別的使人迷惑的) Rust。這就是 async/await
出現的理由。它給予編譯器須要的信息,這些信息用來在異步函數之間調用 borrow check。
async/await
關鍵字在 1.31 被引入,雖然它們目前的實現還不能向下兼容。但大部分工做都已完成,而且你能夠期待這個功能在將要到來的一個版本中可用。
除了爲網絡應用程序實現高效的低層次開發以外,Rust 還能夠在更高層次上實現更高效的開發。
許多服務器須要去處理相同類型的任務。它們須要解析 URL 或者處理 HTTP 任務。若是把它們變成組件通用抽象類型,而且在包之間分享 —— 那麼就能夠輕鬆地把它們組合在一塊兒來組成各類各樣的服務和框架。
爲了驅動組件開發過程,Tide 組件 爲這些組件提供了測試平臺,並最終展現這些組件。
對於命令行工具,工做組須要給更小,更低級的庫引入高級的抽象,而且打磨已有的工具。
對於一些 CLI 腳本,你真的很想使用 bash。例如,若是你僅僅須要喚出其餘 shell 工具並在它們之間使用管道傳輸數據,bash 是最好的選擇。
不過 Rust 也很是適於其餘的命令行工具。好比你正在構建一個複雜的工具像 ripgrep 或構建一個處在現有的庫功能之上的 CLI。
Rust 不須要運行時而且容許你編譯進單獨的靜態二進制文件,這樣利於分發。而且你能獲得其它語言向 C 或 C++ 得不到的高級抽象,這些特性已經讓 Rust CLI 開發者更高效。
工做組須要作些什麼來改善它?是更高級的抽象。
有了這些高級抽象,組合成熟的 CLI 會快速並輕鬆。
這些抽象中的其中一個是 human panic 庫。沒有這個庫,若是你的 CLI 代碼報錯,他可能會輸出整個錯誤棧。但這對於你的終端用戶沒有幫助。你能夠添加自定義錯誤處理,不過那須要額外工做。
若是你用了 human panic,那麼輸出會自動地轉到錯誤轉儲文件。而用戶會看到有幫助的消息建議他們報告這個問題並上傳錯誤轉儲文件。
工做組也讓 CLI 開發更容易入手。例如,confy 庫會自動處理一些新 CLI 工具的安裝事項。它只會問你兩件事:
從這個問題出發,confy 會幫你處理剩餘的事情。
工做組會抽象出不少不一樣的任務,這些任務能夠在不一樣的 CLI 中共用。可是還有更多能夠抽象的。工做組會製做更多相似的高級庫,並隨着時間推移解決更多的 Pager cut bug。
當你體驗一門語言時,你須要經過工具來體驗。從你使用的編輯器開始,它貫穿於開發和維護的各個階段。
這意味着高效的語言依賴於高效工具。
這裏有一些工具(包含一些現有 Rust 工具的改進)會做爲 Rust 2018 的一部分被引進。
固然,高效的關鍵是將代碼從頭腦中迅速傳遞到屏幕上。IDE 的支持嚴重影響這一點。爲了支持 IDE,咱們須要一些工具來告訴 IDE Rust 代碼的實際含義 —— 例如,告訴 IDE 什麼字符串對代碼完成有意義。
在 Rust 2018 推送中,社區聚焦於 IDE 須要的功能。隨着 Rust Language Server 和 IntelliJ Rust 的發展,如今許多 IDE 已經可以對 Rust 有良好的支持。
更快的編譯意味着更高效。因此咱們使編譯器更快。
之前,當你想編譯 Rust 包,編譯器會重複編譯每一個包裏的單獨文件。可是如今,使用增量編譯讓編譯器變得智能而且只會重複編譯已經改變的部分。這和其它的優化一塊兒使得 Rust 編譯器更加迅速。
高效也意味着不須要天天解決風格問題(再不須要去爭論代碼風格規則)。
rustfmt 工具經過使用(已與社區達成共識的)默認代碼風格自動格式化你的代碼。使用 rustfmt 保證你全部的 Rust 代碼符合相同的風格。就像 C++ 使用 clang format 和 JavaScript 使用 Prettier 那樣。
有時候你的身旁能有個經驗豐富的顧問會很是好……給你提出一些代碼的最佳實踐。這就是 Clippy 作的東西 —— 它會審查你的代碼實現並告訴你怎樣讓代碼更符合語言習慣。
可是若是你在維護一個使用過期的語法的老舊代碼庫,那麼只是獲取提示並本身來糾正代碼可能會很乏味。你只是但願有人進入代碼庫來更正這些問題。。
對於這些狀況,rustfix 會自動化該過程。它會同時應用來自 Clippy 等工具的 lint 並更新舊的代碼來匹配 Rust 2018 語法。
生態系統的這些變化已經帶來大量的生產力,可是一些生產力問題只能經過語言自己的變化來解決。
就像我在簡介中說過的,大部分語言層面的變動徹底兼容已有的 Rust 代碼。這些變動都是 Rust 2018 變動的一部分。不過由於它們不會破壞原有任何代碼,它們也能夠在任何 Rust 代碼中運行……甚至是沒有使用 Rust 2018 的代碼。
讓咱們看一些被加到所有版本的重大語言功能。而後咱們能夠看一下 Rust 2018 特點功能的小清單。
這裏有一個重大新語言功能的小型示例,它(或將)被包含在全部的語言版本中
Rust 的一大賣點就是借用檢查器。借用檢查器幫助你確保你的代碼是內存安全的。但對於 Rust 開發新手來講它也一個痛點。
部分緣由是須要學習新的概念。但還有一個大的緣由……借用檢查器有時候會拒絕那些看起來應該工做的代碼,甚至對於那些概念有足夠理解的人來講,他們也會遇到這種狀況。
這是由於一次借用的有效期是到它的做用域結束爲止 —— 例如,變量的有效期到函數結束爲止。
這意味着儘管變量值的有效期已經結束而且不能訪問,別的變量仍然被拒絕直到函數結束。
爲了解決這個問題,咱們使得借用檢查器更加智能。如今它能夠看到實際正在使用的變量。若是它的有效期結束,那麼它不會阻塞其餘使用數據的借用。
不過當前這個特性只支持在 Rust 2018 中使用,而在不遠的未來,全部 Rust 版本都將可使用。以後我很會寫更多關於這部分的內容。
Rust 中的宏已經出如今 Rust 1.0 以前。可是在 Rust 2018 中,咱們對它作了一些重大的改進,好比引入過程宏。
使用過程宏,有點像你能夠添加本身的語法到 Rust。
Rust 2018 帶來了兩種過程宏:
類函數宏容許你擁有看起來像常規函數調用的東西,但這些東西其實是在編譯期間運行的。他們接受一些代碼並輸出不一樣的代碼,而後編譯器將這些代碼插入到二進制文件中。
他們已經存在了一段時間,但你能用它們作的事情有限。你的宏只能獲取輸入代碼並在其上運行匹配語句。它無權查看輸入代碼中的全部令牌。
可是使用過程宏,你能夠得到與解析器相同的輸入 —— 令牌流。這意味着能夠建立更強大的類函數宏。
若是你熟悉 JavaScript 等語言中的裝飾器,屬性宏和它很是類似。它們容許你在 Rust 中註解應該預處理並轉換爲其餘內容的代碼。
derive
宏就是作這種事情的東西。當你把 derive 放到一個結構上時,編譯器會把這個結構輸入(在它被解析爲一個令牌列表以後)並處理它。具體來講,它將從特徵中添加函數的基本實現。
這種變化很是直觀。
之前,若是你想借一些資源並嘗試匹配它,你不得不添加一些奇怪的語法:
但如今,你再也不須要寫 &Some(ref s)
了。你能夠只寫 Some(s)
,Rust 能清楚地找到(它們之間的)差別。
Rust 2018 的最小部分是它特有的功能。如下是 Rust 2018 版本解鎖的少數幾項更改。
Rust 2018 中添加了一些關鍵字。
try
關鍵字async/await
關鍵字這些功能還沒有徹底實現,但對應的關鍵字正在向 Rust 1.31 添加中。這意味着在未來,當咱們實現了這些關鍵字背後的功能時,也不須要引入新的關鍵字(引入關鍵字將會是一個有破壞性的變動。
開發人員學習 Rust 的一個痛點是模塊系統。咱們能夠來看看爲何(是這樣)。(咱們)很難去推斷 Rust 會使用哪一個模塊。
爲了解決這個問題,咱們對 Rust 中路徑的工做方式進行了一些更改。
例如,若是你導入了一個包,則能夠在頂級路徑中使用它。可是,若是你將任何代碼移動到子模塊,那麼它將再也不起做用。
// 頂層模塊
extern crate serde;
// 這樣在頂層模塊中能夠工做
impl serde::Serialize for MyType { ... }
mod foo {
// 但它在子模塊中**不能**工做
impl serde::Serialize for OtherType { ... }
}
複製代碼
另外一個例子是前綴 ::
,它被用來指代當前包的根目錄或一個外部包,這(兩種狀況)可能很難被(使用者)區分。
咱們已經明確了這一點。如今,若是要引用包的根路徑,則使用前綴 crate::
。這只是咱們所作的 path clarity 改進之一。
若是你有現存的 Rust 代碼而且但願它使用 Rust 2018,那麼你極可能須要爲這些新的模塊路徑更新它。但那並不意味着你須要手動更新代碼。在將版本說明符添加到 Cargo.toml 以前運行 cargo fix
,而且運行 rustfix
會爲你進行全部更改。
在 Rust 2018 版本指南 中瞭解這個版本的更多內容
Lin 是 Mozilla Developer Relations 團隊的工程師。她瞭解 JavaScript、WebAssembly、Rust 和 Servo,還能夠繪製代碼漫畫(code cartoons)。
若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。