Rust 學習筆記:泛型

2023年10月16日 · 中文


前言

本系列文書寫我個人初探 Rust 的學習筆記,章節劃分主要基於著名的 The BookThe Rust Programming Language,程式碼部分通常是個人閱讀消化後以方便說明的方式撰寫,完整學習建議直接參見該書。

The Rust Programming Language

該書也有中文翻譯版,不過個人閱讀以英文原版為主以鞏固對 terminology 的一致認識,我認為對未來閱讀以及查找資料會較為順暢。

不論語言該書都是相當優秀的學習資源,選擇適合你的語言開始學習 Rust 吧。

Appendix F: Translations of the Book

泛型

泛型允許我們寫程式時定義一些未來才會指定的類型,這能讓方法或資料類型更有彈性。

function

如下所示,在不使用泛型的情況下,因為我們必須指定參數的類型,即使 function 內的程式邏輯幾乎完全相同我們也必須撰寫兩支 function。

fn main() {
    print_number(5);        // 5
    print_str("My str");    // My str
}

fn print_number(num: i32){
    println!("{num}");
}

fn print_str(s: &str){
    println!("{s}");
}

可以用泛型簡化,泛型以 <> 定義,命名為 T 算是 naming convention,也可以自由命名。

use std::fmt::Display;

fn main() {
    my_print(5);
    my_print("My str");
}

fn my_print<T: Display>(content: T){
    println!("{content}");
}

要注意的是呼叫 println! 需要實作 Display trait,因此這邊將 T 限縮為有實作 Display trait 的類型,trait 在下一章就會學習到,這邊稍微看過即可。

struct

struct 也可以使用泛型,這邊利用了複數個泛型讓 S 能夠儲存任意的資料類型。

#[derive(Debug)]
struct S<T, U> {
    a: T,
    b: U
}


fn main() {
    let x = S {
        a: String::from("test"),
        b: 15
    };
    
    let y = S {
        a: 1.5,
        b: vec![1, 2, 3]
    };
    
    println!("{:?}", x);        // S { a: "test", b: 15 }
    println!("{:?}", y);        // S { a: 1.5, b: [1, 2, 3] }
}

impl

implementation block 中使用泛型實作方法的寫法如下,需要在 impl 後先定義泛型才能為 S<T> 實作方法。

use std::fmt::Display;

struct S<T> {
    a: T,
}

impl<T: Display> S<T>{
    fn print_a(&self){
        println!("{}", self.a);
    }
}


fn main() {
    let x = S {
        a: String::from("test")
    };
    
    x.print_a();        // test
}

如果將 a 設為為實作 Display trait 的類型但呼叫 print_a 則會報錯。

let y = S {
    a: vec![1, 2, 3]
};

y.print_a();            // ERROR
// the method `print_a` exists for struct `S<Vec<{integer}>>`, 
// but its trait bounds were not satisfied

也可以只為特定類型實作方法,下面程式只為 i32 實作 print_a

struct S<T> {
    a: T,
}

impl S<i32>{
    fn print_a(&self){
        println!("{}", self.a);
    }
}

fn main() {
    let x = S {
        a: 5
    };
    
    let y = S {
        a: String::from("test")
    };
    
    x.print_a();        // 5
    y.print_a();        // ERROR
    // no method named `print_a` found for struct `S<String>` in the current scope
}

enum

enum 中使用的泛型已經不陌生,常用的 OptionResult 其實都是以泛型實作。

enum Option<T> {
    Some(T),
    None,
}

enum Result<T, E> {
    Ok(T),
    Err(E),
}