Cargo 使用語義化版本控制原則,並針對 0.x 版本號有特殊處理。Cargo 預設允許版本號彈性,避免版本衝突導致編譯失敗。Cargo.lock 檔案記錄專案使用的每個套件的確切版本,確保構建一致性。更新套件版本需要修改 Cargo.toml 或執行 cargo update 指令。釋出 crate 到 crates.io 需要使用 cargo package 指令建立套件檔案,並在 Cargo.toml 中新增必要的元資料,例如授權條款、首頁和儲存函式庫資訊。大型專案可以利用 Cargo 工作空間分享建置目錄和 Cargo.lock 檔案,節省編譯時間和磁碟空間。Rust 社群也提供了許多額外工具,例如自動檔案生成和託管、持續整合服務,以及 README.md 檔案生成工具,提升 Rust 開發效率。

版本控制與相依性管理

在Rust的套件管理系統Cargo中,版本控制扮演著至關重要的角色。當你在Cargo.toml檔案中指定一個套件的版本時,Cargo會根據特定的相容性規則來選擇合適的版本。

版本相容性規則

Cargo的版本相容性規則根據語義化版本控制(Semantic Versioning)的原則,但有一些特別的處理方式:

  • 0.0開頭的版本號被視為極度不穩定,Cargo不會認為它與其他版本相容。
  • 0.x開頭的版本號(其中x非零)被視為與同一系列的其他點發行版本相容。例如,指定image = "0.6.1",Cargo可能會使用0.6.3版本。
  • 當一個專案達到1.0版本後,只有新的主要版本才會破壞相容性。因此,如果你指定version = "2.0.1",Cargo可能會使用2.17.99版本,但不會使用3.0版本。

相依性版本的彈性

預設情況下,Cargo允許版本號具有一定的彈性,以避免因版本衝突而導致專案無法編譯。例如,如果兩個函式庫libAlibB分別依賴於不同版本的num套件(例如0.1.310.1.29),如果版本號要求完全匹配,那麼任何專案都無法同時使用這兩個函式庫。

版本指定運算元

為了滿足不同的需求,Cargo提供了多種版本指定運算元:

| 版本指定 | 說明 | |





– |













| | image = "=0.10.0" | 只使用確切的0.10.0版本 | | image = ">=1.0.5" | 使用1.0.5或任何更高的版本(甚至是2.9) | | image = ">1.0.5 <1.1.9" | 使用高於1.0.5但低於1.1.9的版本 | | image = "<=2.7.10" | 使用任何低於或等於2.7.10的版本 | | image = "*" | 使用任何版本,直到其他地方有更具體的約束 |

Cargo.lock檔案

為了避免在每次構建時都升級到最新的套件版本,Cargo會在第一次構建專案時生成一個Cargo.lock檔案,記錄所使用的每個套件的確切版本。後續的構建將參考這個檔案,使用相同的版本。

只有當你手動更新了Cargo.toml中的版本號,或執行了cargo update指令,Cargo才會升級到新的相容版本。

$ cargo update
Updating registry `https://github.com/rust-lang/crates.io-index`
Updating libc v0.2.7 -> v0.2.11
Updating png v0.4.2 -> v0.4.3

對於可執行的專案,應該將Cargo.lock檔案提交到版本控制系統,以確保所有開發者使用相同的依賴版本。

釋出Crate到crates.io

當你決定將自己的函式庫釋出為開源軟體時,可以使用cargo package指令建立一個包含所有原始碼檔案和Cargo.toml的套件檔案。

$ cargo package
warning: manifest has no description, license, license-file, documentation,
homepage or repository. See http://doc.crates.io/manifest.html#package-metadata
for more info.
Packaging fern_sim v0.1.0 (file:///.../fern_sim)
Verifying fern_sim v0.1.0 (file:///.../fern_sim)
Compiling fern_sim v0.1.0 (file:///.../fern_sim/target/package/fern_sim-0.1.0)

為了避免警告,你需要在Cargo.toml中新增一些元資料,例如授權條款、首頁和儲存函式庫資訊。

[package]
name = "fern_sim"
version = "0.1.0"
authors = ["You <you@example.com>"]
license = "MIT"
homepage = "https://fernsim.example.com/"
repository = "https://gitlair.com/sporeador/fern_sim"
documentation = "http://fernsim.example.com/docs"
description = """
Fern simulation library.
"""

內容解密:

  • cargo package指令:用於建立一個包含所有原始碼檔案和Cargo.toml的套件檔案,以便釋出到crates.io。
  • Cargo.toml中的[package]區段:包含了函式庫的元資料,如名稱、版本、作者、授權條款等。
  • 釋出Crate的流程:首先使用cargo package建立套件檔案,然後可以將其上傳到crates.io與他人分享。

Rust 結構體詳解:從基礎到進階

Rust 的結構體(structs)是一種用於將多個不同型別的值組合成單一值的資料結構,類別似於 C 和 C++ 中的 struct,Python 中的類別(classes),以及 JavaScript 中的物件(objects)。結構體允許開發者以單元的形式處理多個值,並且可以讀取和修改其個別元件。此外,結構體還可以擁有與其相關聯的方法,用於操作其元件。

結構體的種類別

Rust 提供了三種結構體型別:具名欄位(named-field)、元組式(tuple-like)和單元式(unit-like)結構體。它們的主要區別在於如何參照其元件。

  1. 具名欄位結構體:每個元件都有名稱。
  2. 元組式結構體:元件由其出現順序標識。
  3. 單元式結構體:沒有任何元件。

本章節將詳細介紹每種型別的結構體,並展示它們在記憶體中的表現形式。同時,我們還將介紹如何為結構體新增方法、定義可與多種元件型別配合使用的泛型結構體型別,以及如何要求 Rust 自動為您的結構體生成常見且有用的特徵實作。

具名欄位結構體

具名欄位結構體的定義如下所示:

/// 八位元灰階畫素的矩形。
struct GrayscaleMap {
    pixels: Vec<u8>,
    size: (usize, usize)
}

內容解密:

  • struct GrayscaleMap 定義了一個名為 GrayscaleMap 的結構體。
  • pixels: Vec<u8> 表示一個包含八位元無符號整數的向量,用於儲存畫素資料。
  • size: (usize, usize) 是一個元組,包含兩個 usize 型別的值,分別代表灰階圖的寬度和高度。

這種結構體允許開發者以 GrayscaleMap 為單位進行操作,同時能夠存取和修改其 pixelssize 元件。

發布 Crates 到 crates.io

在討論結構體之前,我們先簡要回顧如何發布 Rust 的程式函式庫(crate)到 crates.io。當您準備好發布您的 crate 時,需要確保 Cargo.toml 檔案正確無誤。特別是,如果您的 crate 依賴其他位於本地路徑的 crate,您需要將這些依賴改為指定版本號碼,以確保其他使用者能夠成功編譯您的 crate。

image = "0.6.1"

或者,您可以同時指定本地路徑和版本號碼:

image = { path = "vendor/image", version = "0.6.1" }

發布前,請登入 crates.io 取得 API 金鑰,並使用 cargo publish 命令上傳您的 crate。

工作空間(Workspaces)

當您的專案不斷增長,包含多個 crate 時,可以使用 Cargo 工作空間來節省編譯時間和磁碟空間。工作空間是一組分享相同建置目錄和 Cargo.lock 檔案的 crate。

要在專案根目錄下建立工作空間,請建立一個 Cargo.toml 檔案,內容如下:

[workspace]
members = ["fern_sim", "fern_img", "fern_video"]

刪除子目錄中的 Cargo.lock 檔案和 target 目錄後,使用 cargo build --all 命令即可建置工作空間中的所有 crate。

更多實用功能

Rust 社群提供了許多額外的便利功能,例如:

  • 自動在 docs.rs 上生成和託管您的 crate 檔案。
  • 使用 Travis CI 在每次推播到 GitHub 時自動建置和測試您的程式碼。
  • 利用第三方 Cargo 外掛程式從 crate 的頂層檔案註解生成 README.md 檔案。

這些工具和社群資源進一步提升了 Rust 在支援大型專案方面的能力。

結語

Rust 的結構體提供了一種靈活的方式來組織和管理複雜的資料。透過瞭解不同型別的結構體及其應用場景,開發者可以更有效地利用 Rust 進行系統程式設計。同時,Cargo 和 crates.io 的使用使得程式函式庫的管理和分享變得更加容易。結合 Rust 社群提供的各種工具和資源,開發者能夠更好地構建和維護大型專案。

Rust 結構體詳解

Rust 語言提供了多種自定義資料結構的方式,其中最基本且最常用的是結構體(Struct)。結構體允許開發者將多個不同型別的變陣列合成一個單一的實體,從而更好地組織和管理程式碼中的資料。

命名欄位結構體

在 Rust 中,結構體可以定義為具有命名欄位的形式,這種形式被稱為命名欄位結構體。例如:

pub struct GrayscaleMap {
    pub pixels: Vec<u8>,
    pub size: (usize, usize)
}

上述程式碼定義了一個名為 GrayscaleMap 的結構體,用於表示一個灰階影像。其中包含兩個公開的欄位:pixelssizepixels 欄位是一個向量,用於儲存影像的畫素資料,而 size 欄位則是一個元組,用於儲存影像的寬度和高度。

內容解密:

  1. pub struct GrayscaleMap:宣告一個名為 GrayscaleMap 的公開結構體,可以在其他模組中被存取。
  2. pub pixels: Vec<u8>:定義一個名為 pixels 的公開欄位,其型別為 Vec<u8>,用於儲存灰階畫素值。
  3. pub size: (usize, usize):定義一個名為 size 的公開欄位,其型別為 (usize, usize),用於儲存影像的寬度和高度。

建立 GrayscaleMap 的例項可以透過結構體表示式來完成,如下所示:

let width = 1024;
let height = 576;
let image = GrayscaleMap {
    pixels: vec![0; width * height],
    size: (width, height)
};

內容解密:

  1. let image = GrayscaleMap { ... }:使用結構體表示式建立一個 GrayscaleMap 例項。
  2. pixels: vec![0; width * height]:初始化 pixels 欄位,建立一個大小為 width * height 的向量,並將所有元素初始化為 0。
  3. size: (width, height):初始化 size 欄位,將影像的寬度和高度儲存為一個元組。

Rust 也提供了簡化的語法來建立結構體例項,當欄位名稱與變數名稱相同時,可以省略欄位名稱,如下所示:

fn new_map(size: (usize, usize), pixels: Vec<u8>) -> GrayscaleMap {
    assert_eq!(pixels.len(), size.0 * size.1);
    GrayscaleMap { pixels, size }
}

內容解密:

  1. GrayscaleMap { pixels, size }:簡化的結構體表示式,等同於 GrayscaleMap { pixels: pixels, size: size }
  2. assert_eq!(pixels.len(), size.0 * size.1):檢查 pixels 向量的長度是否與影像的大小相符。

元組結構體

除了命名欄位結構體之外,Rust 還支援元組結構體(Tuple-Like Struct),其定義方式類別似於元組。例如:

struct Bounds(usize, usize);

建立元組結構體的例項與建立元組類別似,需要指定結構體的名稱:

let image_bounds = Bounds(1024, 768);

存取元組結構體的元素可以使用 . 運算元,就像存取元組的元素一樣:

assert_eq!(image_bounds.0 * image_bounds.1, 786432);

內容解密:

  1. struct Bounds(usize, usize):定義一個名為 Bounds 的元組結構體,包含兩個 usize 型別的元素。
  2. let image_bounds = Bounds(1024, 768):建立一個 Bounds 例項,儲存影像的邊界。
  3. image_bounds.0image_bounds.1:存取 Bounds 例項的元素,分別代表寬度和高度。

元組結構體的元素可以設定為公開或私有,具體取決於需求。例如:

pub struct Bounds(pub usize, pub usize);

這種情況下,Bounds 的元素都是公開的,可以直接被存取。

使用其他結構體初始化新結構體例項

在建立新結構體例項時,可以使用另一個相同型別的結構體來初始化部分欄位。例如,在定義一個掃帚(Broom)結構體時,可以透過複製另一個掃帚的部分屬性來建立新的掃帚例項:

struct Broom {
    name: String,
    height: u32,
    health: u32,
    position: (f32, f32, f32),
    intent: BroomIntent
}

fn chop(b: Broom) -> (Broom, Broom) {
    let mut broom1 = Broom { height: b.height / 2, .. b };
    let mut broom2 = Broom { name: broom1.name.clone(), .. broom1 };
    broom1.name.push_str(" I");
    broom2.name.push_str(" II");
    (broom1, broom2)
}

內容解密:

  1. let mut broom1 = Broom { height: b.height / 2, .. b }:使用另一個 Broom 例項 b 初始化 broom1,並修改其 height 欄位。
  2. let mut broom2 = Broom { name: broom1.name.clone(), .. broom1 }:使用 broom1 初始化 broom2,並克隆 name 欄位以避免所有權轉移。
  3. broom1.name.push_str(" I")broom2.name.push_str(" II"):修改兩個新掃帚的名字,以區分它們。