如何構建一個 Rust 項目

楔子

開新坑啦~~~,雖然目前還有別的坑沒有填,但這並不妨礙我挖新的坑。只挖坑不填坑,雖然很沒有操守,但是很爽。好吧,開個玩笑,皮了一下,我挖了哪些坑心裏都有數,在此保證遲早都會填上。

這次開的新坑是我將和大家一起學習 Rust,並且到後期還會使用 Rust & PyO3 來爲 Python 編寫擴展(突然想起來自己這個公衆號一開始是介紹和 Python 相關的)。

關於 Rust 的特點、優勢和劣勢之類的,這裏就不贅述了,網上一大堆,隨便一搜就是。總之 Rust 確實是出了名的難學,很容易從入門到入墳,但如果在學習的時候改變一下三觀,會發現其實也沒那麼難。

hello rustc

首先我們要安裝 Rust,至於安裝過程可以參考菜鳥教程。

https://www.runoob.com/rust/rust-setup.html

這裏我使用 Rust 版本是 1.62.0,操作系統是 CentOS。值得一提的是,Rust 的穩定性保證了所有發行版本都是向後兼容的,因此即使你使用其它版本的 Rust 也沒有問題。只不過不同版本的 Rust 在編譯時的輸出內容可能會有細微的差異,這是因爲 Rust 在升級的過程中改進了編譯器的錯誤提示信息和警告信息。

那麼下面我們就來打印一個 "Hello world!",感受一下一個最基本的 Rust 程序是什麼樣子的。

// 文件名:main.rs
fn main() {
    println!("Hello world!");
}

最後是打印,我們調用了一個名爲 println! 的宏,注意:println 的結尾有一個 !,而 Rust 中所有以 ! 結尾的調用都意味着你正在使用一個宏。如果結尾沒有 !,那麼調用的就是一個普通函數。

最後我們來編譯這段程序,編譯方式是使用 rustc,可以把它想象成 gcc。

# 直接在 rustc 後面輸入要編譯的文件即可
rustc main.rs

通過 rustc main.rs 即可完成編譯並生成可執行文件,可執行文件的名字叫 main,Windows 則是 main.exe。所以在不指定可執行文件的名稱時,它默認和源文件具有相同的基名稱。

如果想手動指定生成的文件名,可以使用 -o 參數,比如 rustc main.rs -o main。

我們看到在編譯的時候和 C 也非常類似,只需要使用 rustc 將 .rs 文件編譯生成可執行文件,便可以在其它沒有 Rust 環境的機器中運行。

不過我們當前只有一個文件,使用 rustc 還是很方便的,但隨着項目的規模越來越大,協同開發的人員越來越多,那麼管理項目依賴、代碼構建這樣的事情就會變得越來越複雜和瑣碎。

下面就來介紹一個幫助我們簡化問題,並能夠實際運用於生產的 Rust 構建工具:cargo。

hello cargo

cargo 是 Rust 工具鏈中內置的構建系統及包管理器,當你安裝 Rust 的時候,cargo 會自動安裝。由於它可以處理衆多諸如構建代碼、下載編譯依賴庫等瑣碎但重要的任務,所以絕大部分的 Rust 用戶都會選擇它來管理自己的 Rust 項目。

> rustc --version
rustc 1.62.0 (a8314ef7d 2022-06-27)
> cargo --version
cargo 1.62.0 (a748cf5a3 2022-06-08)

因爲我們當前編寫的簡單程序不依賴任何外部庫,所以通過 cargo 來構建一個打印 "Hello world!" 的項目時,它只會用到 cargo 中負責構建代碼的那部分功能。因此初看上去,它和 rustc 並沒有太大的區別,但當你開始嘗試編寫更加複雜的 Rust 程序時,cargo 會讓添加、管理依賴這件事變得十分輕鬆。

使用 cargo 創建一個項目

現在讓我們使用 cargo 創建一個新的項目,功能還是負責打印 "Hello world!",並和之前的做一個對比,來看一看它們之間有何異同。

使用 cargo new 即可創建一個項目,會根據項目名自動生成一個目錄,然後我們進入這個目錄,看看裏面都有什麼。

我們看到目錄裏面有一個 .toml 文件,它是一種配置文件;然後還有一個 src 目錄,該目錄裏面有一個 main.rs。

main.rs 裏面的內容是默認給我們生成的,裏面是一個打印 "hello world!" 的代碼模板,內容如下:

fn main() {
    println!("Hello, world!");
}

打印 "hello world" 算是程序猿之間的標準儀式了。

然後看看 Cargo.toml,裏面都有哪些配置:

[package]
name = "hello_cargo"
version = "0.1.0"
edition = "2021"

[dependencies]

首行文本中的 [package] 是一個區域標籤,它表明接下來的語句會被用於配置當前的程序包。緊隨標籤後的 3 行語句提供了 Cargo 編譯這個程序時需要的配置信息。關於這裏的 Cargo.toml 就不多介紹了,目前先了解一下即可。

最後需要說明的是,Cargo 在創建項目的時候還會自動初始化一個 Git 倉庫,並生成默認的 .gitignore 文件。

對於當前而言,這個 Git 倉庫沒啥卵用,將它刪了也是可以的。

使用 Cargo 構建(build)和運行(run)項目

顯然對於 Cargo 創建的項目而言,我們的源文件應該放在 src 目錄中,而現在 src 裏面正好有一個 main.rs,裏面也是負責打印 "Hello world"。那麼問題來了,我們要如何將這個項目跑起來呢?

首先我們要構建項目,說白了還是生成可執行文件,直接 hello_cargo 目錄下執行 cargo build 即可對項目進行構建,然後會在 ./target/debug 目錄中生成可執行文件,文件名和項目名一致,也叫 hello_cargo。

首次使用命令 cargo build 構建的時候,它還會在項目根目錄下創建一個名爲 Cargo.lock 的新文件,這個文件記錄了當前項目所有依賴庫的具體版本號。由於當前的項目不存在任何依賴,所以這個文件中還沒有太多東西。我們最好不要手動編輯其中的內容,也無需編輯,因爲 cargo 會自動幫助我們維護它。

以上就是構建(build)項目,構建完了再手動運行可執行文件。那麼問題來了,可不可以構建完了自動執行呢?答案是可以的,使用 cargo run 即可,相當於構建 + 運行。

注意:我們對比一下 cargo build 和 cargo run 的輸出,我們看到 cargo run 把 Compiling 這一步給省略了。這是因爲我們之前已經構建過了,並且也沒有修改源文件,所以 cargo 就不會二次構建了。這裏我們修改一下源文件,或者乾脆直接將 target 目錄給刪掉,然後再使用 cargo run 的時候就會重新構建了。

此時就重新構建了,根據輸出也能看出相比 cargo build,cargo run 就是單純的多了 Running。

所以對於當前而言,cargo run 就等價於 cargo build && target/debug/hello_cargo。

cargo check

另外,cargo 還提供了一個 cargo check 的命令,可以使用這個命令來快速檢查當前的代碼是否可以通過編譯,而不需要花費額外的時間去真正生成可執行程序。

你也許會好奇,我們爲什麼需要這樣一個命令?通常來講,由於 cargo check 跳過了生成可執行程序的步驟,所以它的運行速度要遠遠快於 cargo build。可以看一下輸出,cargo check 只花費了 0.16 秒,但是之前的 cargo build 則花了 0.83 秒。

假如你在編碼的過程中需要不斷通過編譯器檢查錯誤,那麼使用 cargo check 就會極大地加速這個過程。事實上,大部分 Rust 用戶在編寫程序的過程中都會週期性地調用 cargo check 以保證自己的程序可以通過編譯,只有真正需要生成可執行程序時纔會調用 cargo build。

Rust 編譯器非常嚴格,如果代碼能通過編譯器的檢測,那麼做個簡單的單元測試基本就能達到上線的標準了,所以編寫 Rust 程序就是一個不斷和 Rust 編譯器鬥智鬥勇的過程。

讓我們總結一下目前接觸到的關於 cargo 的知識點:

1)使用 cargo new 項目名 即可新建一個項目,會自動創建一個同名目錄,該目錄下有一個 src 目錄、一個 Cargo.toml 和一個 Git 倉庫。Cargo.toml 我們暫時還用不到,用到的時候再說;然後 src 是存放源代碼的地方,我們的代碼直接寫在 src 裏面即可。

2)進入項目目錄中使用 cargo build 即可進行構建,構建的結果會存儲在 target/debug 目錄下。

3)用 cargo run 同樣可以完成構建,並且在構建完畢之後會自動運行可執行文件。

4)使用 cargo check 可以快速地對我們的代碼進行檢查。

再回顧一下 rustc,對於當前的 hello_cargo 項目,如果我們不使用 cargo 而是使用 rustc 該怎麼做呢?

很明顯,如果只是單個文件的話,那麼 rustc 會稍微方便一些;但如果項目一大、文件一多,肯定還是要使用 cargo。對於那些由多個包構成的複雜項目而言,使用 cargo 來協調整個構建過程要比手動操作簡單得多。

並且使用 cargo 的一個好處就是,它的命令是不變的,始終是 cargo build, cargo run,與你使用的操作系統種類、項目規模無關。

以 release 模式構建

當準備好發佈自己的項目時,還可以使用 cargo build --release 在優化模式下構建並生成可執行程序。它生成的可執行文件會被放置在 target/release 目錄下,而不是之前的 target/debug 目錄。

這種模式會以更長的編譯時間爲代價來優化代碼,從而使代碼擁有更好的運行時性能,這也是存在兩種不同的構建模式的原因。一種模式用於開發,它允許你快速地反覆執行構建操作。而另一種模式則用於構建交付給用戶的最終程序,這種構建場景不會經常發生,但卻需要生成的代碼擁有儘可能高效的運行時表現。

值得指出的是,假如你想要對代碼的運行效率進行基準測試,那麼請用 cargo run --release 命令進行構建,並使用 target/release 目錄下的可執行程序完成基準測試。

小結

以上就是 cargo 相關的內容,瞭解完 rustc 和 cargo 之後,我們就可以開始學習 Rust 的數據類型了。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/4VAAhNvpzDUlEVfDkqa5GQ