iT邦幫忙

2023 iThome 鐵人賽

DAY 14
0

如果我們今天定義了一個方法,想用於不同的型態,就可以使用 Trait

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

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

上一篇
Day 13 Match
下一篇
Day 15 Ownership part 1
系列文
成為程式界的 F1 賽車手,用 30 天認識 Rust 30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
hello world
iT邦新手 5 級 ‧ 2023-09-29 20:24:22

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的概念可以參考高大的文章

感謝回饋,已修正文章!

我要留言

立即登入留言