十年 Python 程式設計師,初次嘗試 Rust:「非常優秀!」

csdn 發佈 2022-11-24T18:21:41.910355+00:00

在 M1 上,我推薦在 Visual Studio Code 中使用 LLDB,它不僅工作良好,通常還要比在輸出結果中列印日誌更為方便。

摘要:Python 和 Rust,都是近幾年深受開發者喜愛的程式語言,那麼作為一個擁有十年 Python 編程經驗的開發者來說,初次嘗試 Rust 會有怎樣的感受呢?

連結

:https://karimjedda.com/carefully-exploring-rust/

聲明:本文為 CSDN 翻譯,未經允許禁止轉載。

作者 | Karim Jedda

譯者 | 彎月 責編 | 鄭麗媛

出品 | CSDN(ID:CSDNnews)

最近,我找到了一份新工作,公司最常用的程式語言之一是 Rust。

在此之前,我使用 Python 長達十年之久,主要是做數據工程的工作。但如今,我打算嘗試一下這種新的(對我來說)程式語言。我經常在不同平台上看到各類誇讚 Rust 的文章,我想看看 Rust 是否真的不負盛名。

Rust 與 Python 有很大的不同,因此我不打算在本文中詳細說明 Rust 的獨特之處。作為初學者,我只希望儘快上手,希望能以最短的過渡,儘快用 Rust 完成工作,同時也希望評估一下我自己的學習能力。

從某種程度上來說,我更感興趣的是 Rust 整體的使用體驗,而不是具體的功能列表。

設置開發環境

設置開發環境非常簡單,只需參照 Rust 網站提供的示例,在終端中運行一個命令就可以了。

當你認為一切都已安裝並配置妥當,此時如果想驗證 Rust 是否已正確安裝,只需在空目錄中創建一個空項目:

cargo new tutorialcd tutorialcargo run

接下來,在文本編輯器中打開該文件夾。如果你是新手,我推薦 VSCode,因為其中的一些擴展很有幫助,關於如何使用這些擴展的指南也很容易入手。我推薦 rust-analyzer 作為 VSCode 的唯一擴展。

輸出與調試

如果想了解程序是如何運行的,首先要做的就是通過命令行來了解程序在幹什麼,以及完成了什麼。

此外,你還可以使用常規調試器。在 M1 上,我推薦在 Visual Studio Code 中使用 LLDB,它不僅工作良好,通常還要比在輸出結果中列印日誌更為方便。

到這裡為止,Rust 與 Python 其實都非常相似,只不過所有命令都是通過 cargo run 運行的,而不是調用特定文件,如 python3 somefile.py。

另外,你也可以先運行 cargo build,然後運行 target/debug/tutorial 中的文件,得到的結果是相同的。接下來,如果將生成的文件複製到另一個位置或另一台類似的機器上,也可以正常工作,且無需安裝任何與 Rust 相關的東西。

錯誤處理

不得不承認,編程中總會遇到一些意外,能夠以可預測的方式處理這些意外非常重要。編程中的一大挑戰就是很難考慮到程序中所有可能出現的錯誤,因為只要寫代碼就可能會出錯。

「每個人都知道調試比編寫程序要難一倍。所以,如果你在代碼編寫代碼時就用盡了聰明才智,又如何調試呢?」

—— Brian W. Kernighan

在 Python 中,通常我們通過 try/except 方法來拋異常,並完成錯誤處理。我們運行一段代碼,如果出錯,則通過條件來捕捉異常,如果所有條件都不匹配,則將其放入一個通用的異常中。異常有各種不同的類別,Python 允許你在包中調用不存在的函數,並在運行時產生異常,但在 Rust 中這樣做甚至無法通過編譯——Rust 不允許在運行時出現任何奇怪的錯誤,從而消除了一大類不太容易預測的錯誤。

下面通過一個例子來說明 Rust 的這種方法,同時我會用 Python 的術語進行解釋。

在上面的代碼中,我們創建了一個自定義的異常,在 do_something 函數中拋出,而 main 函數會檢查該異常。上面的代碼跟 try/exept 基本上一樣,只不過多了一些樣板代碼(這些樣板代碼是必須的,但以我現在的水平有點難以理解)。

你也許會說「肯定有更好的辦法」,特別是如果你有很多 Python 經驗的話,的確如此,我們將不得不使用包。。

使用外部包

與其他行業相比,編程的最大優勢就是可以使用別人構建的東西。如果你計劃在程序中進行錯誤處理,那麼有一個很好用的包 thiserror。Rust 的包管理器是 cargo。

Rust 中的包叫做 crate。安裝方法為編輯目錄下的 Cargo.toml 文件。在本例中,我們在 [dependencies] 後面添加 thiserror = "1.0" 就可以了。

然後可以像下面這樣重寫之前的代碼:

現在代碼看起來很正常。與一切從頭開始相比,我更喜歡這種做法。

我花了四五年時間才找到用 Python 編程的樂趣,所以我也願意多花些時間來探索 Rust 的高級功能。Rust 有許多錯誤處理的方法,而我喜歡更簡單的方法。

我有意略過了一些簡單的概念,比如「什麼是 enum?」 「pub 是什麼意思?」 「那些#標記是什麼」等,因為你只需運行一下代碼就能明白它們的意思。

一切看起來都還不錯。那麼,測試方面又如何呢?

編寫測試

測試應該從單元測試和集成測試兩個級別上著手。實現方法有好幾種,雖然你可以把測試代碼和 Rust 代碼放在同一個文件中(這也是官方指南的推薦),但我還是希望用一個單獨的文件夾來組織所有測試代碼,這樣可以減少閱讀代碼時的負擔,也可以減少編輯文件時占用的屏幕面積。而且說實話,在編寫測試和編寫代碼時,我的心情是不一樣的。

方法之一如下:

然後可以用 cargo test 運行測試,結果如下:

還有許多值得展開講的地方,但為了避免過於複雜,我們點到為止,這算是「帕累托最優」(又稱80/20法則)。這讓我想起了 pytest,一個能即刻提高舒適度的工具。

讀取文件、運行一些代碼並寫入另一個文件

以上,我們討論了一些最基本的問題:輸出,調試,使用外部包,以及測試。下面,我們來做一些更有效率的事情:我們可以寫一個程序來處理本地文件。下面的例子將會讀取 csv 文件,計算一些數值,然後將輸出結果。

為了實現該程序,我們需要在 Cargo.toml 中添加以下兩行設置:

  • csv="1.1"

  • serde={version="1", features=["derive"]}

你可以猜猜 main 函數應該怎樣寫。

當然,這個程序還可以實現更多功能。如果你有一個非常複雜的 CSV 文件,則可以在 Rust 中調用 pandsa(pola.rs)來處理數據。我還需要進一步研究,不過似乎這種處理方法非常強大且高效。

我認為,與 Python 相比,Rust的 CSV 處理能力不相上下,除了它能自動反序列化之外。

最後,我們還可以添加一些測試,此處不再贅述。

發送 HTTP 請求

下面,我們來嘗試發送基本的 HTTP 請求並處理結果。現在的絕大多數請求都需要處理 JSON。

在 Cargo.toml 中添加如下幾行代碼:

reqwest = { version = "0.11", features = ["json"] }tokio = { version = "1", features = ["full"]}serde_json = "1"

這樣就可以了,現在可以從 API 請求數據了。結合使用上面兩個方法,我們可以獲取數據,用 pola.rs 進行分析,然後將結果寫入 CSV 文件中,同時保證內存安全。還記得 Python 需要循環才能實現這一點嗎?在這方面 Rust 做得很好。

我相信,Rust 的生態系統會越來越大,以覆蓋更多的用例,以後利用已有的 crate 實現這一切會易如反掌。

使用 SQLite

雖然在這篇文章中提到 SQLite 似乎有些奇怪,但我開發過的程序經常用到 SQLite。我很喜歡 SQLite,因為它是可移植的,非常有效,而且不需要任何維護。

用 Python 操作 SQLite 的問題永遠是要不要使用 ORM。請不要誤會,SQLAlchemy 非常優秀,但在進行非常小的操作時,用它就像殺雞用牛刀了。而且 SQLAlchemy 帶來的複雜性使它不適合小型嵌入式設備。

反之, Rust 可以在這方面大放異彩,網上有很多如何利用Rust操作 SQLite 的例子,我認為都非常不錯。

舉個簡單的例子,別忘了在 Cargo.toml 中添加下面這行代碼:

rusqlite = { version = "0.28.0", features = ["bundled"] }

該例子來自 rusqlite crate。當然,這只是冰山一角。但組合以上幾種方法,就可以實現許多很有用的功能了。

總結

綜合考慮,Rust 是一個非常優秀的語言,有許多優秀的包,非常感謝發明這門語言並為之努力貢獻的開發者們。雖然這篇文章只是對 Rust 做了初步的探索,但我希望拋磚引玉,讓初學者產生學習 Rust 的興趣。

初次使用某種程式語言時,重點在於弄清楚語言本身能實現哪些功能,而不是背誦一篇完整的術語表。你不需要去理解 borrowing、繼承或 traits 的具體含義,而應該跟隨些入門文章按部就班地做一遍。

從無到有的難度遠大於從一到十。

關鍵字: