如果我們今天定義了一個方法,想用於不同的型態,就可以使用 Trait
我們先定義一個 trait ,並在裡面定義方法
另外再定義出一個資料型態
我們會用 impl 的方式,為這個型態掛上 trait
讓這個型態可以使用裡面的方法
fn main() {
    trait Calculator {
        fn method_one(&self);
        fn method_two(&self, arg: i32) -> bool;
    }
    struct Numbers {
        value: i32,
    }
    impl Calculator for Numbers {
        fn method_one(&self) {
            println!("{}", self.value)
        }
        fn method_two(&self, arg: i32) -> bool {
            if arg > 0 {
                println!("positive number");
                return true;
            } else {
                println!("negative number");
                return false;
            }
        }
    }
    let instance = Numbers { value: 32 };
    instance.method_one();
    instance.method_two(instance.value);
}
        
> cargo run
32
positive number
那如果我們有多種型態呢?
一樣可以用 impl 來幫他們擴充
fn main() {
    trait CandyShop {
        fn method_one(&self);
    }
    struct Fudge {
        flavor: String,
    }
    struct Ingredient {
        main: String,
        second: String,
    }
    impl CandyShop for Fudge {
        fn method_one(&self) {
            println!("My candy shop has sell {} of Fudge", self.flavor)
        }
    }
    impl CandyShop for Ingredient {
        fn method_one(&self) {
            println!("Our Fudge is made of {}, and {}", self.main, self.second)
        }
    }
    let mocha_fudge = Fudge {
        flavor: "mocha".to_string(),
    };
    mocha_fudge.method_one();
    let mocha_fudge_ingredient = Ingredient {
        main: "milk".to_string(),
        second: "sugar".to_string(),
    };
    mocha_fudge_ingredient.method_one();
}
我們可以根據不同型態,在 function 中做不同的事情
執行後就會依照不同型態,跑出不同的結果
> cargo run
My candy shop has sell mocha of Fudge
Our Fudge is made of milk, and sugar
如果之前有學習過物件導向的程式語言
trait 就有點像是 module
我們可以幫型態來擴充他們的特徵
另外我們還要提到 derive
derive 是 rust 中的 macro 之一,
裝上它之後會自動依照型態實現 Rust 內建的 trait,
需要指定 trait 給他,但如果這個型態不能用,就會噴錯
下方我們指定使用 Copy 以及 Clone , derive 就會幫我們自動實現 Copy 以及 Clone 這兩個 trait
fn main() {
    trait Calculator {
        fn method_one(&self);
        fn method_two(&self, arg: i32) -> bool;
    }
    #[derive(Copy, Clone)]
    struct Numbers {
        value: i32,
    }
    impl Calculator for Numbers {
        fn method_one(&self) {
            println!("{}", self.value)
        }
        fn method_two(&self, arg: i32) -> bool {
            if arg > 0 {
                println!("positive number");
                return true;
            } else {
                println!("negative number");
                return false;
            }
        }
    }
    let instance = Numbers { value: 32 };
    let instance_copy = instance;
    instance_copy.method_one();
    instance_copy.method_two(instance_copy.value);
}
> cargo run
32
positive number
derive 的概念,我的理解是幫忙產生code,所以幫忙產生code需要用到的是macro巨集。只是剛好rust提供了一些內建可以擴充的巨集,可以放置在dervie裡。或是我們有需求的話,也可以自己寫巨集放進derive裡。和Copy Clone不是同一件事。
Copy和Clone是雖然都是複製,但是兩個有些處理上的不同,主要會體現在所有權上面。Copy主要是在處理基礎型別,或是編譯時使用記憶體空間已知的類別,如i32, u32, f64等,我們甚至不能自己實作Copy trait,而實現Copy trait的變數,rust在指派時會Copy一份,也就是所有權不會交出:
let mut a = 3; // i32類別 放Stack的資料型別
let mut b = a; // 因為有Copy所以這裡Copy了
b += 10;
println!("a: {}, b: {}", a, b);
// 印出 a: 3, b: 13
let mut a = "hello".to_string();  // 字串,放Heap
let mut b = a;         // 因為資料是Heap,沒有實現Copy trait,這裡會移交所有權
b.push_str(" world");
println!("a: {}, b: {}", a, b);  // 這裡會報錯,因為變數a在第2行就死了
因為放Heap的資料rust不會幫我們在Stack中直接Copy,所以如果我們想要完整複製一份資料就要實作clone來進行複製,struct只有所有欄位都實現Copy trait它才可以derive Copy,比如:
#[derive(Clone, Copy)]  // 這裡會報錯,因為String不能Copy
struct Student {
    no: i32,
    name: String,
}
Stack或Heap的概念可以參考高大的文章
感謝回饋,已修正文章!