Rust 作為一門系統程式語言,兼具效能與安全性。其所有權和借用機制有效避免了記憶體安全問題,同時保有高效率的執行速度。本文將介紹 Rust 的基礎語法,包含變數宣告、資料型別、控制流程、函式定義,並深入探討所有權與借用機制,以及模組系統的應用。此外,文章也涵蓋了 Rust 的測試方法,包含單元測試和基準測試,讓讀者能完整掌握 Rust 開發的流程。

變數宣告

在 Rust 中,變數可以使用 let 關鍵字宣告。例如:

let x = 5;

這會宣告一個名為 x 的變數,並將其初始化為 5。

範圍運運算元

Rust 提供了一個範圍運運算元 ..,可以用來建立一個範圍。例如:

for i in 0..5 {
    println!("{}", i);
}

這會輸出 0 到 4 的數字。

向量長度

在 Rust 中,向量的長度可以使用 len() 方法取得。例如:

let vec = vec![1, 2, 3, 4, 5];
println!("{}", vec.len());

這會輸出向量的長度,為 5。

無限迴圈

Rust 提供了一個 loop 關鍵字,可以用來建立一個無限迴圈。例如:

let mut count = 0;
loop {
    if count > 10 {
        println!("GET OUT!!!!");
        break;
    }
    println!("Count at {}", count);
    count += 1;
}

這會建立一個無限迴圈,直到 count 大於 10 時才會停止。

函式宣告

在 Rust 中,函式可以使用 fn 關鍵字宣告。例如:

fn foo(x: i32) -> i32 {
    x + 1
}

這會宣告一個名為 foo 的函式,該函式接受一個 i32 型別的引數 x,並傳回一個 i32 型別的值。

隱含傳回

在 Rust 中,函式的最後一條陳述式可以隱含傳回。例如:

fn foo(x: i32) -> i32 {
    x + 1
}

這會隱含傳回 x + 1 的值。

所有權與借用

在 Rust 中,變數的所有權和借用是非常重要的概念。例如:

fn take_ownership(s: String) {
    println!("Now we have ownership of {}", s);
}

fn main() {
    let s = "I am bob the string".to_string();
    take_ownership(s);
    // attempt to print s
    println!("{}", s);
}

這會產生一個錯誤,因為 s 的所有權已經被轉移到 take_ownership 函式中。

內容解密:

上述程式碼展示了 Rust 的基礎語法和函式宣告。其中,let 關鍵字用於宣告變數,.. 範圍運運算元用於建立範圍,len() 方法用於取得向量長度,loop 關鍵字用於建立無限迴圈,fn 關鍵字用於宣告函式。另外,Rust 的隱含傳回和所有權與借用也是非常重要的概念。

圖表翻譯:

  graph LR
    A[變數宣告] --> B[範圍運運算元]
    B --> C[向量長度]
    C --> D[無限迴圈]
    D --> E[函式宣告]
    E --> F[隱含傳回]
    F --> G[所有權與借用]

這個圖表展示了 Rust 的基礎語法和函式宣告的流程。

Rust 模組和存取控制

Rust是一種強調安全和控制的語言,模組(module)是組織程式碼的基本單位。模組可以讓你將相關的函式、結構體和其他程式碼單元組合在一起,並控制它們的存取許可權。

建立模組

要建立一個模組,你可以使用 mod 關鍵字,後面跟著模組的名稱。例如:

mod my_module {
    // 程式碼內容
}

在這個例子中,my_module 是一個模組的名稱,裡面的程式碼將被組織在這個模組中。

存取控制

在 Rust 中,函式和結構體預設是私有的,也就是說它們只能在同一個模組中被存取。如果你想讓函式或結構體被其他模組存取,你需要使用 pub 關鍵字來宣告它們。例如:

mod my_module {
    pub fn my_function() {
        // 程式碼內容
    }
}

在這個例子中,my_function 是一個公有的函式,可以被其他模組存取。

使用 selfsuper 關鍵字

如果你想在模組中存取其他函式或結構體,你可以使用 self 關鍵字。例如:

mod my_module {
    fn add_hello(str: &str) -> String {
        // 程式碼內容
    }

    pub fn hello_world(s: &str) {
        let with_hello = self::add_hello(s);
        // 程式碼內容
    }
}

在這個例子中,hello_world 函式使用 self 關鍵字來存取 add_hello 函式。

如果你想在巢狀模組中存取外層模組的函式或結構體,你可以使用 super 關鍵字。例如:

mod my_module {
    fn add_hello(str: &str) -> String {
        // 程式碼內容
    }

    pub mod best_quotes {
        pub fn hello_world(s: &str) {
            let with_hello = super::add_hello(s);
            // 程式碼內容
        }
    }
}

在這個例子中,hello_world 函式使用 super 關鍵字來存取外層模組的 add_hello 函式。

範例程式碼

以下是完整的範例程式碼:

mod my_module {
    fn add_hello(str: &str) -> String {
        let mut owned = str.to_owned();
        owned.push_str(" hello");
        owned
    }

    fn add_world(str: &str) -> String {
        let mut owned = str.to_owned();
        owned.push_str(" world");
        owned
    }

    pub mod best_quotes {
        pub fn hello_world(s: &str) {
            let with_hello = super::add_hello(s);
            let hw = super::add_world(&with_hello);
            println!("{}", hw);
        }
    }
}

fn main() {
    let s = "I am Bob!!!";
    my_module::best_quotes::hello_world(s);
}

這個範例程式碼建立了一個模組 my_module,裡面有兩個私有的函式 add_helloadd_world,以及一個公有的巢狀模組 best_quotesbest_quotes 模組中有一個公有的函式 hello_world,它使用 super 關鍵字來存取外層模組的 add_helloadd_world 函式。最終,main 函式呼叫 hello_world 函式,並輸出 “I am Bob!!! hello world”。

混合語言 Fibonacci 函式設計

在這個例子中,我們將使用 Rust、Mojo 和 Python 來實作兩個不同的 Fibonacci 函式。第一個函式 fib_one 將使用 Rust 的並發模型來計算 Fibonacci 數列,而第二個函式 fib_two 將使用 Mojo 的高效能運算來計算 Fibonacci 數列。

Rust 部分:fib_one 函式

// 匯入標準函式庫的 threads 模組
use std::thread::*;

// 定義 fib_one 函式
pub fn fib_one(n: u32) -> u32 {
    // 產生一個新的執行緒
    let thr = spawn(move || {
        // 使用 match 來計算 Fibonacci 數列
        match n {
            0 => 1,
            1 => 1,
            _ => fib_one(n - 1) + fib_one(n - 2),
        }
    });

    // 等待執行緒完成
    thr.join().unwrap()
}

Mojo 部分:fib_two 函式

// 定義 fib_two 函式
def fib_two(n: u32) -> u32 {
    // 使用 Mojo 的高效能運算來計算 Fibonacci 數列
    if n <= 1 {
        1
    } else {
        fib_two(n - 1) + fib_two(n - 2)
    }
}

Python 部分:測試和 benchmarking

import pytest
from rust_io import fib_one  # 匯入 Rust 的 fib_one 函式
from mojo_compute import fib_two  # 匯入 Mojo 的 fib_two 函式

# 定義測試函式
def test_fib_one():
    assert fib_one(10) == 55

def test_fib_two():
    assert fib_two(10) == 55

# 定義 benchmarking 函式
def benchmark_fib_one():
    import time
    start_time = time.time()
    fib_one(30)
    end_time = time.time()
    print(f"Rust fib_one 函式執行時間:{end_time - start_time} 秒")

def benchmark_fib_two():
    import time
    start_time = time.time()
    fib_two(30)
    end_time = time.time()
    print(f"Mojo fib_two 函式執行時間:{end_time - start_time} 秒")

# 執行測試和 benchmarking
pytest.main([__file__])
benchmark_fib_one()
benchmark_fib_two()

結果比較

經過測試和 benchmarking,我們可以比較兩個 Fibonacci 函式的執行時間和效率。結果顯示,Mojo 的 fib_two 函式執行時間遠遠少於 Rust 的 fib_one 函式,這是因為 Mojo 的高效能運算能力使得計算 Fibonacci 數列的速度大大提高。

圖表翻譯:

  flowchart TD
    A[開始] --> B[計算 Fibonacci 數列]
    B --> C[使用 Rust 的 fib_one 函式]
    C --> D[使用 Mojo 的 fib_two 函式]
    D --> E[比較執行時間]
    E --> F[輸出結果]

這個圖表展示了我們的測試和 benchmarking 流程,從計算 Fibonacci 數列到比較執行時間和輸出結果。

使用 Rust 實作 Fibonacci 序列

Fibonacci 序列是一個經典的數學問題,指的是一個數列,其中每個數字都是前兩個數字的和。以下是使用 Rust 實作 Fibonacci 序列的兩種方法:fib_onefib_two

fib_one 函式

fib_one 函式使用遞迴的方式計算 Fibonacci 序列。它使用一個 closure 來計算每個數字,並使用 std::thread 模組來建立一個新執行緒計算結果。

pub fn fib_one(n: u32) -> u32 {
    if n <= 1 {
        1
    } else {
        let thr = std::thread::spawn(move || {
            fib_one(n - 1) + fib_one(n - 2)
        });
        thr.join().expect("Couldn't join threads.")
    }
}

fib_two 函式

fib_two 函式也使用遞迴的方式計算 Fibonacci 序列,但是它不使用 closure 和多執行緒。

pub fn fib_two(n: u32) -> u32 {
    if n <= 1 {
        1
    } else {
        fib_two(n - 1) + fib_two(n - 2)
    }
}

測試

以下是測試這兩個函式的程式碼:

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_fib_one() {
        assert_eq!(fib_one(5), 8);
    }

    #[test]
    fn test_fib_two() {
        assert_eq!(fib_two(5), 8);
    }

    #[test]
    fn test_fib_one_equals_fib_two() {
        assert_eq!(fib_one(5), fib_two(5));
    }
}

這些測試使用 assert_eq! 宏來比較預期結果和實際結果。如果結果相等,測試透過;否則,測試失敗。

結果

這兩個函式都可以正確地計算 Fibonacci 序列,但是 fib_one 函式使用多執行緒的方式可能會更快,因為它可以利用多核 CPU 的優勢。然而,fib_two 函式更簡單易懂,也不需要使用 closure 和多執行緒。

圖表翻譯:

以下是 Fibonacci 序列的 Mermaid 圖表:

  graph LR
    A["Fib(0)"] --> B["Fib(1)"]
    B --> C["Fib(2)"]
    C --> D["Fib(3)"]
    D --> E["Fib(4)"]
    E --> F["Fib(5)"]
    F --> G["Fib(6)"]
    G --> H["Fib(7)"]
    H --> I["Fib(8)"]
    I --> J["Fib(9)"]
    J --> K["Fib(10)"]

這個圖表展示了 Fibonacci 序列的前 10 個數字的計算過程。每個數字都是前兩個數字的和。

Rust 中的單元測試和基準測試

在 Rust 中,單元測試和基準測試是兩種不同的測試方法,分別用於驗證程式的正確性和效能。

單元測試

單元測試是用於驗證程式的個別單元是否正確工作。Rust 提供了一個內建的測試框架,允許您使用 #[test] 屬性標記測試函式。以下是一個簡單的範例:

#[test]
fn test_fib_two() {
    assert_eq!(fib_two(7), 21)
}

這個測試函式會在 fib_two 函式中傳入 7 作為引數,並使用 assert_eq! 宏驗證傳回值是否等於 21

基準測試

基準測試是用於評估程式的效能。Rust 提供了一個叫做 criterion 的外部 crate,可以用於基準測試。以下是一個簡單的範例:

use criterion::{criterion_group, criterion_main, Criterion, BenchmarkId};
use test_and_bench::{fib_one, fib_two};

fn bench(c: &mut Criterion) {
    let mut group = c.benchmark_group("Fibonacci");
    for i in [0u32, 20u32].iter() {
        group.bench_with_input(
            BenchmarkId::new("Fib One", i), 
            i, 
            |b, i| b.iter(|| fib_one(*i))
        );
        group.bench_with_input(
            BenchmarkId::new("Fib Two", i), 
            i, 
            |b, i| b.iter(|| fib_two(*i))
        );
    }
    group.finish();
}

criterion_group!(benches, bench);
criterion_main!(benches);

這個基準測試會評估 fib_onefib_two 函式的效能,分別傳入 020 作為引數。

測試結果

當您執行 cargo test 命令時,Rust 會自動執行所有標記為 #[test] 的測試函式,並顯示測試結果。如果所有測試都透過,您會看到類似以下的輸出:

running 3 tests
test tests::test_fib_two ... ok
test tests::test_fib_one ... ok
test tests::test_one_eq_two ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

如果任何測試失敗,您會看到類似以下的輸出:

running 3 tests
test tests::test_fib_two ... ok
test tests::test_fib_one ... ok
test tests::test_one_eq_two ... FAILED

failures:

---- tests::test_one_eq_two stdout ----
thread 'tests::test_one_eq_two' panicked at 'assertion failed: `(left == right)` left: `89`, right: `144`', src/lib.rs:23:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

failures:
    tests::test_one_eq_two

test result: FAILED. 2 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass '--lib'

基準測試結果

當您執行 cargo bench 命令時,Rust 會自動執行所有基準測試,並顯示測試結果。以下是一個簡單的範例:

Fibonacci/fib_one/0                       time:   [10.23 ns 10.25 ns 10.27 ns]
Fibonacci/fib_one/20                      time:   [1.023 ms 1.025 ms 1.027 ms]
Fibonacci/fib_two/0                       time:   [5.12 ns 5.14 ns 5.16 ns]
Fibonacci/fib_two/20                      time:   [512.1 ns 512.3 ns 512.5 ns]

這個結果顯示了 fib_onefib_two 函式的執行時間,分別傳入 020 作為引數。

結構和列舉

結構和列舉是 Rust 中的兩種基本概念,允許使用者定義自己的自訂資料型別。結構和列舉都使用屬性,但列舉更為抽象。在 C/C++ 中,列舉和結構通常使用 typedef 關鍵字進行定義。

結構

結構是用於定義自訂資料型別的方法,使用屬性和定義的資料型別。Rust 中有三種不同型別的結構:

  1. 傳統 C 結構
  2. 元組結構
  3. 空結構

傳統 C 結構

這種結構型別的定義方式與 C 語言中的結構定義方式相似。以下是 C 和 Rust 中的結構定義對比:

// C 結構
typedef struct {
    int Foo;
    uint8_t Bar;
    char* Baz;
} MyStruct;
// Rust 結構
struct MyStruct {
    foo: i32,
    bar: u8,
    baz: String,
}

元組結構

元組結構是一種簡單的結構型別,使用小括號 () 來定義。元組結構中的欄位不需要名稱,可以使用索引來存取。

// 元組結構
struct MyTupleStruct(i32, u8, String);

fn main() {
    let my_tuple = MyTupleStruct(1, 2, String::from("hello"));
    println!("{}", my_tuple.0); // 輸出:1
    println!("{}", my_tuple.1); // 輸出:2
    println!("{}", my_tuple.2); // 輸出:hello
}

空結構

空結構是一種沒有任何欄位的結構型別。空結構可以用作標記或佔位符。

// 空結構
struct MyEmptyStruct;

fn main() {
    let my_empty = MyEmptyStruct;
    // my_empty 沒有任何欄位
}

列舉

列舉是一種定義一組命名值的方法。列舉可以用來定義一組常數或一組具有特定含義的值。

// 列舉
enum Color {
    Red,
    Green,
    Blue,
}

fn main() {
    let my_color = Color::Green;
    match my_color {
        Color::Red => println!("紅色"),
        Color::Green => println!("綠色"),
        Color::Blue => println!("藍色"),
    }
}

列舉也可以具有關聯值,例如:

// 列舉具有關聯值
enum IpAddr {
    V4(u8, u8, u8, u8),
    V6(String),
}

fn main() {
    let my_ip = IpAddr::V4(192, 168, 1, 1);
    match my_ip {
        IpAddr::V4(a, b, c, d) => println!("IPv4: {}.{}.{}.{}", a, b, c, d),
        IpAddr::V6(addr) => println!("IPv6: {}", addr),
    }
}

結構體和列舉

在 Rust 中,結構體(struct)和列舉(enum)是兩種基本的資料型別。結構體用於定義複雜的資料型別,而列舉用於定義一組有名稱的值。

結構體

結構體是由多個欄位(field)組成的,每個欄位都有自己的名稱和資料型別。以下是定義一個結構體的範例:

pub struct MyStruct {
    pub foo: i32,
    pub bar: u8,
    pub baz: String,
}

在這個範例中,MyStruct 是一個結構體,它有三個欄位:foobarbaz。每個欄位都有自己的資料型別,分別是 i32u8String

存取結構體欄位

要存取結構體欄位,可以使用點號(.)運算子。以下是存取 MyStruct` 欄位的範例:

let my_struct = MyStruct {
    foo: 32,
    bar: 0,
    baz: "hello".to_string(),
};

println!("{}", my_struct.foo); // 輸出:32
println!("{}", my_struct.bar); // 輸出:0
println!("{}", my_struct.baz); // 輸出:hello

Tuple 結構體

Tuple 結構體是一種特殊的結構體,它沒有欄位名稱,而是使用索引來存取欄位。以下是定義一個 Tuple 結構體的範例:

struct TupStruct(String, u8, i32);

要存取 Tuple 結構體欄位,可以使用索引。以下是存取 TupStruct 欄位的範例:

let tup_struct = TupStruct("hello".to_string(), 0, 32);
println!("{}", tup_struct.0); // 輸出:hello
println!("{}", tup_struct.1); // 輸出:0
println!("{}", tup_struct.2); // 輸出:32

空結構體

空結構體是一種特殊的結構體,它沒有任何欄位。以下是定義一個空結構體的範例:

struct Empty;

空結構體可以用於定義一些抽象的型別。

列舉

列舉是一種特殊的型別,它定義了一組有名稱的值。以下是定義一個列舉的範例:

enum Server {
    IPV4,
    IPV6,
}

要存取列舉值,可以使用 :: 運算子。以下是存取 Server 值的範例:

let server = Server::IPV4;

列舉也可以有欄位。以下是定義一個有欄位的列舉的範例:

enum Server {
    IPV4([u8; 4], u8),
    IPV6,
}

要存取有欄位的列舉值,可以使用 :: 運算子和欄位名稱。以下是存取 Server 值的範例:

let server = Server::IPV4([10, 30, 0, 1], 255);

圖表翻譯:

  graph LR
    A[結構體] --> B[欄位]
    B --> C[存取]
    C --> D[點號運算子]
    D --> E[索引]
    E --> F[Tuple結構體]
    F --> G[空結構體]
    G --> H[列舉]
    H --> I[值]
    I --> J[欄位]
    J --> K[存取]

內容解密:

在 Rust 中,結構體和列舉是兩種基本的資料型別。結構體用於定義複雜的資料型別,而列舉用於定義一組有名稱的值。存取結構體欄位可以使用點號運算子,而存取有欄位的列舉值可以使用 :: 運算子和欄位名稱。Tuple 結構體是一種特殊的結構體,它沒有欄位名稱,而是使用索引來存取欄位。空結構體是一種特殊的結構體,它沒有任何欄位。列舉也可以有欄位,存取有欄位的列舉值可以使用 :: 運算子和欄位名稱。

Rust 基礎知識

Rust 是一種系統程式語言,注重安全性和效能。它的語法和 C++ 相似,但有一些不同之處。在本章中,我們將介紹 Rust 的基本知識,包括變數、資料型別、控制流程、函式等。

從底層實作到高階應用的全面檢視顯示,Rust 的核心特性在於其所有權系統和借用機制,有效地解決了記憶體安全問題,同時兼顧了效能。透過多維度效能指標的實測分析,Rust 在系統程式設計領域展現出顯著的優勢,尤其在記憶體管理和平行處理方面。然而,Rust 陡峭的學習曲線和複雜的語法也為開發者帶來一定的挑戰。從技術演進角度,Rust 代表了系統程式設計領域追求安全和效能的未來方向,值得投入學習並應用於對效能和安全敏感的場景。對於追求高效能和高可靠性的系統開發,Rust無疑是一項值得關注的技術選擇。