返回文章列表

Rust 集合型別與模組系統

本文探討 Rust 的核心集合型別 Vector、String 和 HashMap,以及 Rust 的模組系統、套件和 Crates 管理。文章涵蓋集合型別的建立、更新、存取和遍歷方法,並詳細說明 Rust 模組系統的組織結構、路徑使用和 `use` 關鍵字的應用,同時也講解了套件的結構、依賴管理以及二進位制和函式庫

程式語言 Rust

Rust 提供了 Vector、String 和 HashMap 等核心集合型別,滿足各種資料儲存和操作需求。Vector 作為動態陣列,可透過 pushpop 等方法管理元素,並使用索引或 get 方法安全地存取元素。String 型別支援字串操作,包含建立、更新和合併等功能,String 型別可變,&str 型別不可變。HashMap 則提供了高效的鍵值對儲存和查詢機制,透過 insertget 方法操作鍵值對。Rust 的模組系統則讓程式碼組織更具結構性,透過套件、Crates 和模組的組合,搭配路徑和 use 關鍵字,有效管理程式碼的複雜度和可維護性,並控制程式碼的可見範圍。

Rust 程式語言中的集合型別

Rust 提供多種集合型別以滿足不同的程式需求,包括向量(Vector)、字串(String)以及雜湊對映(HashMap)。這些集合型別是 Rust 程式語言中非常重要的組成部分。

向量(Vector)

向量是一種動態陣列,可以在執行時期改變大小。向量提供了一系列方法來操作其元素,如 pushpop 等。

更新向量元素

可以使用 push 方法在向量尾部新增元素,使用 pop 方法移除向量尾部的元素。

fn main() {
    let mut v1 = vec![1, 2, 3];
    v1.push(4);
    println!("v1 = {:?}", v1); // 輸出:v1 = [1, 2, 3, 4]
    
    let mut v2 = vec![5, 6, 7];
    let popped = v2.pop();
    println!("v2 = {:?}, popped = {:?}", v2, popped); // 輸出:v2 = [5, 6], popped = Some(7)
}

內容解密:

  • vec![1, 2, 3] 建立了一個包含元素 1、2、3 的向量。
  • v1.push(4)v1 的尾部增加了元素 4。
  • v2.pop() 移除並回傳 v2 的最後一個元素,若向量為空則回傳 None

存取向量元素

可以使用索引或 get 方法來存取向量中的元素。使用索引存取元素可能會導致越界錯誤,而使用 get 方法則會回傳一個 Option 列舉,可以安全地處理可能不存在的元素。

fn main() {
    let v = vec![2.5, 10.5, 1.0];
    println!("The Second element = {}", v[1]); // 輸出:The Second element = 10.5
    println!("The Second element = {:?}", v.get(1)); // 輸出:The Second element = Some(10.5)
    println!("The Fourth element = {:?}", v.get(3)); // 輸出:The Fourth element = None
}

內容解密:

  • v[1] 使用索引直接存取第二個元素,若索引無效會導致執行時期錯誤。
  • v.get(1) 使用 get 方法存取第二個元素,回傳 Some(10.5) 表示成功存取到元素。
  • v.get(3) 試圖存取第四個元素,由於索引無效,回傳 None

字串(String)

Rust 中的字串有兩種型別:&strString&str 是字串切片,是對儲存在其他地方的字串的參照,且不可修改。String 則是可增長、修改的字串型別,儲存在堆積上。

建立字串

可以使用 String::new 建立空字串,或使用 String::from 建立具有初始值的字串。

fn main() {
    let string1 = String::new(); // 建立空字串
    let string2 = String::from("Rust"); // 建立字串 "Rust"
    println!("string1 = {} \nstring2 = {}", string1, string2);
}

內容解密:

  • String::new() 建立一個空字串。
  • String::from("Rust") 建立一個包含 “Rust” 的字串。

更新字串

可以使用 pushpush_str+ 運算元或 format!巨集來修改或合併字串。

fn main() {
    let mut str = String::from("Hello");
    str.push('!'); // 在尾部新增單個字元
    println!("str = {}", str); // 輸出:str = Hello!
    
    let mut str2 = String::from("Hello");
    str2.push_str(", World!"); // 在尾部新增字串切片
    println!("str2 = {}", str2); // 輸出:str2 = Hello, World!
    
    let str3 = String::from("Hello");
    let str4 = String::from(", World!");
    let str5 = str3 + &str4; // 使用 + 運算元合併字串
    println!("str5 = {}", str5); // 輸出:str5 = Hello, World!
    
    let str6 = String::from("Hello");
    let str7 = String::from("World");
    let str8 = format!("{}, {}!", str6, str7); // 使用 format! 巨集格式化字串
    println!("str8 = {}", str8); // 輸出:str8 = Hello, World!
}

內容解密:

  • push('!') 在字串尾部新增單個字元 ‘!’。
  • push_str(", World!") 在字串尾部新增字串切片 “, World!"。
  • str3 + &str4 使用 + 運算元將兩個字串合併,注意這裡會消耗 str3 的所有權。
  • format!巨集用於格式化字串,類別似於 println!,但回傳格式化後的字串而非直接輸出。

雜湊對映(HashMap)

雜湊對映是一種鍵值對的集合,使用雜湊函式來儲存和快速查詢鍵值對。

建立雜湊對映

可以使用 HashMap::new() 建立新的雜湊對映,並使用 insert 方法插入鍵值對。

use std::collections::HashMap;

fn main() {
    let mut capital_cities = HashMap::new();
    capital_cities.insert("India", "Delhi");
    capital_cities.insert("United States", "Washington DC");
    capital_cities.insert("England", "London");
    capital_cities.insert("India", "New Delhi"); // 更新鍵 "India" 對應的值
    println!("{:?}", capital_cities);
}

內容解密:

  • HashMap::new() 建立一個新的空雜湊對映。
  • capital_cities.insert(key, value) 在雜湊對映中插入或更新鍵值對。

存取雜湊對映中的值

可以使用 get 方法根據鍵來存取對應的值,它回傳一個 Option<&V>,表示可能存在或不存在的值。

use std::collections::HashMap;

fn main() {
    let mut capital_cities = HashMap::new();
    capital_cities.insert("India", "New Delhi");
    capital_cities.insert("United States", "Washington DC");
    
    let capital_usa = capital_cities.get("United States");
    let capital_china = capital_cities.get("China");
    
    println!("Capital city of USA is: {:?}", capital_usa); // 輸出:Some("Washington DC")
    println!("Capital city of China is: {:?}", capital_china); // 輸出:None
}

內容解密:

  • capital_cities.get(key) 根據鍵查詢對應的值,回傳 Some(value) 若鍵存在,否則回傳 None

遍歷雜湊對映

可以使用 for 迴圈遍歷雜湊對映中的所有鍵值對。

use std::collections::HashMap;

fn main() {
    let mut capital_cities = HashMap::new();
    capital_cities.insert("India", "New Delhi");
    capital_cities.insert("United States", "Washington DC");
    
    for (key, value) in &capital_cities {
        println!("{} : {}", key, value);
    }
}

內容解密:

  • 使用 for (key, value) in &capital_cities 可以遍歷雜湊對映中的所有鍵值對,並印出每個鍵和對應的值。

Rust 的模組系統:組織程式碼的利器

Rust 的模組系統提供了一套強大的工具,幫助開發者組織和管理程式碼,使其更加模組化和可維護。在本章中,我們將探討 Rust 模組系統的各個組成部分,包括套件(Packages)、箱(Crates)、模組(Modules)以及路徑(Paths)。

瞭解 Rust 的模組系統

隨著程式的規模和複雜度增加,有效地組織程式碼變得至關重要。Rust 的模組系統為此提供了多種工具,如箱、套件、模組和路徑。在接下來的章節中,我們將詳細探討這些功能。

套件(Packages)

在 Rust 中,套件是一組提供相關功能的箱的集合。套件由位於根目錄的 Cargo.toml 檔案定義,該檔案指定了套件的依賴關係以及其他後設資料,如套件名稱、版本和作者。

箱(Crates)

箱是 Rust 程式碼的基本編譯單元,可以是函式庫(Library)或可執行檔(Executable)。當我們使用 cargo new 命令建立一個新的 Rust 專案時,Cargo 會為我們建立一個新的箱。

使用模組組織程式碼

模組允許我們將相關的功能組織在一起,使程式碼更加結構化和易於管理。我們可以使用 mod 關鍵字來定義模組,並使用路徑來存取其他模組中的函式或變數。

// 定義一個名為 garden 的模組
mod garden;

// 在 garden 模組中定義一個名為 vegetables 的子模組
// garden.rs 或 garden/mod.rs
pub mod vegetables;

// 在 vegetables 子模組中定義一個函式
// garden/vegetables.rs 或 garden/vegetables/mod.rs
pub fn some_function() {
    println!("This is a function in the vegetables submodule.");
}

fn main() {
    // 使用路徑存取 vegetables 子模組中的函式
    garden::vegetables::some_function();
}

路徑(Paths)

路徑用於存取模組中的函式或變數。Rust 支援兩種路徑:絕對路徑和相對路徑。絕對路徑從箱的根開始,而相對路徑則相對於當前的模組。

// 絕對路徑
crate::garden::vegetables::some_function();

// 相對路徑
garden::vegetables::some_function();

使用 use 關鍵字簡化路徑

use 關鍵字允許我們將模組或函式引入當前作用域,從而簡化路徑。

use garden::vegetables;

fn main() {
    vegetables::some_function();
}

Rust 中的套件與 Crates 管理

Rust 的套件管理系統是透過 Cargo 這個工具來進行管理的。Cargo 不僅能夠處理依賴關係,還能夠編譯和發布套件。在 Rust 中,套件(Package)和 Crates 是兩個不同的概念,但它們緊密相關。

套件(Package)

一個 Rust 套件是由一個或多個 Crates 組成的,並且至少包含一個 Crate。套件的根目錄下必須包含一個 Cargo.toml 檔案,用於定義套件的後設資料、依賴關係和其他設定。

套件結構

  • src/lib.rs:套件的函式庫 Crates 的根檔案,可選。
  • Cargo.toml:套件的設定檔案,定義了套件的後設資料、依賴和其他設定。

依賴管理

Cargo.toml 檔案中的 [dependencies] 部分,可以指定套件所依賴的其他 Crates。例如:

[dependencies]
serde = "1.0"

這指定了該套件依賴於 serde Crates,版本為 1.0 或更高。

Crates

Crates 是 Rust 中的編譯單元,可以編譯成二進位制可執行檔案或函式庫檔案。Crates 可以是二進位制 Crates 或函式庫 Crates。

二進位制 Crates

二進位制 Crates 編譯成可執行檔案,包含一個 main 函式作為入口點。使用 cargo new --bin <crate_name> 可以建立一個新的二進位制 Crates。

建立二進位制 Crates
cargo new --bin binary-crate

這會建立一個名為 binary-crate 的新套件,並產生以下目錄結構:

.
├── Cargo.toml
└── src
    └── main.rs

Cargo.toml 檔案內容如下:

[package]
name = "binary-crate"
version = "0.1.0"
edition = "2021"

[dependencies]

src/main.rs 檔案內容如下:

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

使用 cargo build 編譯後,會在 target/debug 目錄下生成可執行檔案。

函式庫 Crates

函式庫 Crates 編譯成 .rlib 檔案,可供其他 Crates 使用。使用 cargo new --lib <crate_name> 可以建立一個新的函式庫 Crates。

建立函式庫 Crates
cargo new --lib my-library-crate

這會建立一個名為 my-library-crate 的新套件,並產生以下目錄結構:

.
├── Cargo.toml
└── src
    └── lib.rs

src/lib.rs 中定義公開的函式或型別:

pub fn hello_lib_crate() {
    println!("Hello, from library crate!");
}

編譯後,會在 target/debug 目錄下生成 libmy_library_crate.rlib 檔案。

使用函式庫 Crates

要在其他 Crates 中使用函式庫 Crates,需要在 Cargo.toml 中新增對應的依賴,並使用 use 陳述式將需要的函式或型別引入作用域。

在同一套件中使用函式庫 Crates

可以在同一套件中建立一個新的二進位制 Crates 來呼叫函式庫 Crates 中的函式。

src/bin/main.rs 中:

use my_library_crate::hello_lib_crate;

fn main() {
    println!("Hello, from Binary crate ...");
    hello_lib_crate();
}

圖表翻譯:

此圖示呈現瞭如何在 Rust 中使用 Cargo 建立和管理不同型別的 Crates,以及如何透過 Cargo.toml 組態檔案來管理專案的依賴和編譯設定。

圖表翻譯: 此圖表顯示了 Rust 專案的結構,包括 Cargo.toml 組態檔案和 src 目錄下的原始碼檔案。圖中詳細展示瞭如何透過 Cargo 管理專案的依賴關係和編譯過程。

Rust 模組系統詳解

Rust 的模組系統是組織程式碼的重要工具,它允許開發者將程式碼分成可管理的單元,並控制這些單元的可見性。在本章中,我們將探討 Rust 的模組系統,包括模組的定義、存取控制、路徑以及 use 關鍵字的使用。

模組的定義與存取控制

在 Rust 中,模組是用來組織程式碼的基本單位。模組可以包含函式、結構體、列舉等專案。預設情況下,模組是私有的,這意味著它們只能在定義它們的 crate 內部存取。要使模組公開,可以在 mod 關鍵字前加上 pub 關鍵字。

pub mod cook_food {
    pub mod chinese_food {
        pub fn cook_spring_rolls() {
            println!("Spring rolls cooked...");
        }
        pub fn cook_noodles() {
            println!("Noodles cooked...");
        }
    }
    // ... 其他子模組的定義
}

內容解密:

  • pub mod cook_food 定義了一個公開的模組 cook_food
  • cook_food 內部,我們定義了多個子模組,如 chinese_foodindian_fooditalian_food
  • 每個子模組內部都包含多個公開的函式,如 cook_spring_rollscook_noodles

模組的組織結構

隨著專案的成長,模組的數量可能會增加,這時將每個模組放在單獨的檔案中可以提高程式碼的可讀性和可維護性。Rust 允許我們將模組組織成一個層次結構,並將每個模組放在單獨的檔案或資料夾中。

.
├───Cargo.toml
├───src
│   ├───main.rs
│   ├───cook_food.rs
│   └───cook_food
│       ├───chinese_food.rs
│       ├───indian_food.rs
│       └───italian_food.rs

內容解密:

  • src/cook_food.rs 檔案包含了 cook_food 模組的宣告。
  • src/cook_food 資料夾包含了 cook_food 模組的子模組,每個子模組都有自己的檔案。

路徑與 use 關鍵字

要存取定義在其他模組中的專案,我們需要使用路徑。Rust 中的路徑有兩種:絕對路徑和相對路徑。

  • 絕對路徑從 crate 根開始,例如 crate::cook_food::chinese_food::cook_spring_rolls
  • 相對路徑從當前模組開始,例如 cook_food::chinese_food::cook_spring_rolls

使用 use 關鍵字可以將專案引入當前作用域,從而簡化程式碼。

mod cook_food;
use crate::cook_food::chinese_food::cook_spring_rolls;
use crate::cook_food::indian_food::cook_samosa;

fn main() {
    println!("Cooking some delicious food ...");
    cook_spring_rolls();
    cook_samosa();
}

內容解密:

  • mod cook_food; 宣告了 cook_food 模組。
  • use crate::cook_food::chinese_food::cook_spring_rolls;cook_spring_rolls 函式引入當前作用域。
  • main 函式中,我們可以直接呼叫 cook_spring_rollscook_samosa 函式。