學 Rust 要有大局觀

Crash Rust

學習一種新的語言,首先需要了解的就是該語言的基本設計思路,工程架構特點,本文希望可以幫住大多數對 Rust 感興趣的同學快速進入具體工程開發,並掃清大部分除基本語法之外的障礙; 具體涉及到的主題包括安裝,運行,發佈,三方包引入等等;

Rust 安裝

install

安裝 Rust 非常簡單,只需要一條命令,但是注意部分機器 curl 版本可能導致命令執行失敗,比如梁小孩自己的開發機 ubuntu20.04 自帶的 curl 提示 ssl 443 錯誤,如果遇到的話,嘗試重新安裝 curl

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

check

執行 Rustup 命令, 注意如果是第一次全新安裝, 先執行 source $HOME/.cargo/env

root@ecs-x86 :~/docs/Rust/CrashRustIn2Hours# source $HOME/.cargo/env
root@ecs-x86 :~/docs/Rust/CrashRustIn2Hours# rustup
Rustup 1.24.2 (755e2b07e 2021-05-12)
...

Hello Rust!

接下來我們完成第一個 Rust 項目,功能只有一句打印輸出,但是大家在此章節應該學到的是如何編譯並運行 Rust 代碼程序;

編碼

創建第一個rs文件 (hello.rs),文件內容如下:

fn main() {
    println!("hello Rust!");
}

文件目錄結構簡單,代碼目錄下只有一個單獨的hello.rs文件如下:

root@ecs-x86 CrashRustIn2Hours# tree code/
code/
└── hello.rs
0 directories, 1 file

編譯 & 執行

Rust 的編譯器叫做rustc,編譯時直接後跟待編譯的rs文件即可, 不執行輸出文件名則生成同名的可執行文件, 可以直接執行輸出文件得到程序結果

root@ecs-x86 code# rustc hello.rs 
root@ecs-x86 code# tree 
.
├── hello
└── hello.rs
0 directories, 2 files
root@ecs-x86 code# ls -lhF
total 3.3M
-rwxr-xr-x 1 root root 3.3M Jun  2 13:32 hello*
-rw-rw-rw- 1 root root   42 Jun  2 13:29 hello.rs
root@ecs-x86 code# ./hello 
hello Rust!

到此你看到的是純手工開發流程,包括編譯,文件創建,目錄組織等等都需要自行維護,爲了方便工程化,並且快速創建工程,Rust 提供了自己的自動化工具鏈cargo;

Hello Cargo!

cargo之於Rust猶如npm之於nodecargo可以幫助你維護包依賴關係,安裝三方包,自動編譯代碼,執行結果,完善debugrelease的各種部署需求;

cargo三板斧:

cargo init 自動創建工程,包括基本的配置文件,main.rs生成等等

root@ecs-x86 code# cargo init
     Created binary (application) package
root@ecs-x86 code# tree
.
├── Cargo.toml
└── src
    └── main.rs
1 directory, 2 filese

cargo run 自動編譯並執行; 注意,此時編譯選項均爲 debug 模式,所以 target 目錄下只有一個 debug 目錄自動生成 (可執行文件亦在其中)

root@ecs-x86 code# cargo run
   Compiling code v0.1.0 (/root/docs/Rust/CrashRustIn2Hours/code)
    Finished dev [unoptimized + debuginfo] target(s) in 1.34s
     Running `target/debug/code`
Hello, world!
root@ecs-x86 code# tree -L 2
.
├── Cargo.lock
├── Cargo.toml
├── src
│   └── main.rs
└── target
    ├── CACHEDIR.TAG
    └── debug

cargo build 單獨編譯,如果需要單獨 build 可能是最終發佈二進制程序,此時一般附帶參數--release

root@ecs-x86 code# cargo build --release
   Compiling code v0.1.0 (/root/docs/Rust/CrashRustIn2Hours/code)
    Finished release [optimized] target(s) in 0.35s
root@ecs-x86 code# tree -L 2
.
├── Cargo.lock
├── Cargo.toml
├── src
│   └── main.rs
└── target
    ├── CACHEDIR.TAG
    ├── debug
    └── release
4 directories, 4 files

crate 和 module

crate是 rust 對外分發和代碼共享的單位,類似 jar 包,類似 so 庫,是三方庫的概念 •crate是編譯打包的概念 •crate的核心標誌就是一個單獨的Cargo.toml文件,是一個邏輯可編譯的功能庫 •cargo package可以在target/package/目錄下生成對應的*.crate打包文件 •module是模塊的概念,是代碼組織方式,類似於 c++ 的namespace, 類似於 golang 的package的概念 •module的核心標誌是語法層面的use <module_name>的導入和聲明 •module有三種文件組織方式 (假設建立一個叫做string_util的 module)1. 內嵌文件中使用mod string_util { ... }的方式進行定義, 內部可以包含任意多函數,結構體等等 2. 建立一個獨立文件名string_util.rs,內部無上面的顯式mod <module_name>聲明 3. 建立一個文件夾string_util內部包含一個mod.rs文件,還有其他submodule的話文件夾中一般使用第二種創建新文件 • 三者取其一,如果發現都沒有或者有多重情況定義同一個mod的時候 rustc 便會報錯.

關於crate的其他理解可以參考官方 doc 的部分描述:

A crate is a compilation unit in Rust. Whenever rustc some_file.rs is called, some_file.rs is treated as the crate file. If some_file.rs has mod declarations in it, then the contents of the module files would be inserted in places where mod declarations in the crate file are found, before running the compiler over it. In other words, modules do not get compiled individually, only crates get compiled.

Rust thinks in modules, not files. There is no such thing as file imports, Important concepts in the Rust Module System are packagescratesmodules, and paths

學習modulecrate的時候需要了解的基本前提知識如下:

rust 的pubilcprivate的作用域控制結構體層面提升到了mod範圍,結構體本身沒有類似的概念,這一點相比 c++,java,golang 均不同,此處的修改省去了friend 友元getttersetter等等一些列不必要的麻煩, 同一個module內部的所有結構體函數等等可以緊密合作,簡單直接,對外暴露的函數和類型單獨增加pub關鍵字導出, 優雅而且編碼友好

crate 的直觀理解

rust 共享代碼模塊的公網地址爲crates.io, 如果使用過mavenrpm等類似工具應該對此類地址並不陌生;

現在我們拆解一個crates.io上下載最多的rand模塊看一下其中的目錄結構,瞭解一下一個 rust crate 大致有什麼樣的目錄和文件結構

➜  rand git:(master) tree -L 1
.
├── CHANGELOG.md
├── COPYRIGHT
├── Cargo.toml       # 核心的Cargo.toml文件
├── LICENSE-APACHE
├── LICENSE-MIT
├── README.md
├── SECURITY.md
├── benches
├── examples
├── rand_chacha
├── rand_core       # 一個單獨的子crate
├── rand_distr
├── rand_hc
├── rand_pcg
├── rustfmt.toml
├── src              # `rand`的src目錄 
└── utils
9 directories, 8 files


## lib.rs 是比較特殊的一個文件名,一個crate一般只有一個,是默認的crate的導出點和入口文件.
➜  rand git:(master) tree -L 1 src
src
├── distributions
├── lib.rs
├── prelude.rs
├── rng.rs
├── rngs
└── seq
3 directories, 3 files
## 仔細研究`rand_core`你會發現它和外部的`rand`是同構的
➜  rand git:(master) tree -L 2 rand_core
rand_core
├── CHANGELOG.md
├── COPYRIGHT
├── Cargo.toml        # `rand_core`的`Cargo.toml` crate描述文件
├── LICENSE-APACHE
├── LICENSE-MIT
├── README.md
└── src
    ├── block.rs
    ├── error.rs
    ├── impls.rs
    ├── le.rs
    ├── lib.rs      # `rand_core`的`src/lib.rs`文件
    └── os.rs
1 directory, 12 files

可以看到,rand crate本身是以外部依賴其他crate(比如rand_core), 我們自己寫代碼的時候也可以按照這種方式組織代碼,雖然工程上看代碼屬於同一個目錄結構和工程內,但是實際上其中的代碼編譯關係是有明確的分割關係的. 因爲crate本質上是類庫的同等地位,所以一個crate只允許一個lib (lib.rs) 編譯入口文件,但是可以允許有多個main函數(依賴核心的 lib.rs)單獨編譯可執行文件 (binary crate);

註釋和文檔

相比於 C++ 等古老的語言生態,rust 的生態支持解決了太多痛點,比如crates.io的存在保證了代碼的編寫,發佈,文檔的一體化,一切都是簡單的 cargo 命令即可完成;既然說到了文檔,就要知道 cargo 還有cargo doc命令,只要使用markdown編寫的註釋便可直接生成對應的html文檔,其開發友好程度讓人大呼過癮.

rust 的註釋寫法簡單說有如下幾種:

// 普通註釋,此類註釋cargo doc會忽略 •/// 文檔註釋 ,一般函數結構提元素的註釋寫法,內容可以是 markdown 語法,如果包含代碼段,代碼會被自動生成測試代碼 •//! 高層的文檔註釋,此類註釋生成的是高層模塊描述和簡介

cargo new --lib myutil
# 修改src/lib.rs爲如下內容
//! # The first line
//! The second line
/// Adds one to the number given.
///
/// # Examples
///
/// ```
/// let five = 5;
///
/// assert_eq!(6, add_one(5));
/// # fn add_one(x: i32) -> i32 {
/// #     x + 1
/// # }
/// ```
pub fn add_one(x: i32) -> i32 {
    x + 1
}

以上面代碼爲例,生成文檔的過程如下:

cargo doc --no-deps
cargo doc --open

 得到的自動化文檔如下:

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