楔子
接下來我將和你一起學習 Rust,並且到后期還會使用 Rust & PyO3 來為 Python 編寫擴展。
關於 Rust 的特點、優勢和劣勢之類的,這里就不贅述了,網上一大堆,隨便一搜就是。總之 Rust 確實是出了名的難學,很容易從入門到入墳,但如果在學習的時候改變一下三觀,會發現其實也沒那么難。
hello rustc
首先我們要安裝 Rust,至於安裝過程可以參考菜鳥教程。
這里我使用的 Rust 版本是 1.68.2,值得一提的是,Rust 的穩定性保證了所有發行版本都是向后兼容的,因此即使你使用其它版本的 Rust 也沒有問題。只不過不同版本的 Rust 在編譯時的輸出內容可能會有細微的差異,這是因為 Rust 在升級的過程中改進了編譯器的錯誤提示信息和警告信息。
那么下面我們就來打印一個 "Hello world!",感受一下一個最基本的 Rust 程序是什么樣子的。
// 文件名:main.rs
fn main() {
println!("Hello world!");
}
- Rust 源文件以 .rs 結尾,當前的文件名為 main.rs;
- Rust 和 C 一樣,可以使用 // 進行單行注釋,也可以使用 /*...*/ 進行多行注釋;
- 類似於 C,Rust 也要求程序中必須有一個 main 函數,它是程序的主入口,所有的代碼都會從這個入口函數開始執行;
- 函數使用 fn 進行聲明,函數體放在大括號內部。
最后是打印,我們調用了一個名為 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 -V
rustc 1.68.2 (9eb3afe9e 2023-03-27)
> cargo -V
cargo 1.68.2 (6feb7c9cf 2023-03-26)
因為我們當前編寫的簡單程序不依賴任何外部庫,所以通過 cargo 來構建一個打印 "Hello world!" 的項目時,它只會用到 cargo 中負責構建代碼的那部分功能。因此初看上去,它和 rustc 並沒有太大的區別,但當你開始嘗試編寫更加復雜的 Rust 程序時,cargo 會讓添加、管理依賴這件事變得十分輕松。
使用 cargo 創建一個項目
現在讓我們使用 cargo 創建一個新的項目,功能還是負責打印 "Hello world!",並和之前的做一個對比,來看一看它們之間有何異同。
使用 cargo new 即可創建一個項目,會根據項目名自動生成一個目錄,然后我們進入這個目錄,看看里面都有什么。
我們看到該目錄里面有一個 .toml 文件,.toml 文件是一種配置文件,然后還有一個 src 目錄,該目錄里面有一個 main.rs。
main.rs 里面的內容是默認給我們生成的,里面是一個打印 "hello world!" 的代碼模板,內容如下:
fn main() {
println!("Hello, world!");
}
打印 "hello world" 算是程序猿之間的標准儀式了。
然后看看 Cargo.toml,里面都有哪些配置:
[package]
name = "project01"
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 build --release 命令進行構建,並使用 target/release 目錄下的可執行程序完成基准測試。
小結
以上就是 cargo 相關的內容,了解完 rustc 和 cargo 之后,我們就可以開始學習 Rust 的數據類型了。關於數據類型,下一篇文章再聊。