Rust中的三種多態(tài)性——Enum和Trait(上)

0x01 開篇

多態(tài)性(Polymorphism)在很多語言中都存在,比如Java/C#等。有了編程語言的多態(tài)性會使我們在工作中更加靈活和方便。當然,Rust也有多態(tài)性的特點。在Rust中有三種主要方法來實現(xiàn)多態(tài),我們的目的就是討論每種方法的優(yōu)缺點。文章總共分為三章。

下面以一個經(jīng)典的多態(tài)問題為例進行討論。

有三種形狀,矩形、等邊三角形,圓,分別求出他們的面積和周長。

0x02 枚舉

enum是Rust中的一種數(shù)據(jù)結(jié)構(gòu),我們可以將三種形狀保存到一個enum中。示例代碼如下:

enum Shape {
    Rectangle { width: f32, height: f32 },
    Triangle { side: f32 },
    Circle { radius: f32 },
}

接下來為Shape增加兩個方法來分別計算周長和面積。示例代碼如下:

impl Shape {

    /// 計算周長
    pub fn perimeter(&self) -> f32 {
        match self {
            Shape::Rectangle { width, height } => width * 2.0 + height * 2.0,
            Shape::Triangle { side } => side * 3.0,
            Shape::Circle { radius } => radius * 2.0 * std::f32::consts::PI
        }
    }

    /// 計算面積
    pub fn area(&self) -> f32 {
        match self {
            Shape::Rectangle { width, height } => width * height,
            Shape::Triangle { side } => side * 0.5 * 3.0_f32.sqrt() / 2.0 * side,
            Shape::Circle { radius } => radius * radius * std::f32::consts::PI
        }
    }
}

最后,我們可以在代碼中使用它了。示例代碼如下:

/// 輸出周長
fn calc_perimeters(shape: Shape) {
   println!("{}", shape.perimeter());
}

/// 輸出面積
fn calc_area(shape: Shape) {
    println!("{}", shape.area());
}
優(yōu)點

使用enum可以將這些形狀全部放在同一塊內(nèi)存區(qū)域中。這是Rust中實現(xiàn)多態(tài)性最直接的一種方法。它具有下面一些優(yōu)勢:

  • 它的數(shù)據(jù)結(jié)構(gòu)是內(nèi)聯(lián)的,它不需要通過其它引用來獲取它。enum中所有的變量在內(nèi)存中都是連續(xù)存儲的,可以很容易檢索到他們。它可以快速定位變量在內(nèi)存的位置,這也是本文最重要的一個主題。
  • 盡管數(shù)據(jù)是內(nèi)聯(lián)的,但集合中的每個項都可以從其相鄰項中獲取不同的變體。
  • 可以將它們作為原始數(shù)據(jù)使用。
缺點
  • 如果不同變體的大小存在很大的差異,則可能會浪費一些內(nèi)存。
  • 枚舉一旦定義后,就不會被改變,且不能被擴展。這也是非常大的一個缺陷了。

0x03 Trait

直接先上代碼。

trait Shape {
    fn perimeter(&self) -> f32;
    fn area(&self) -> f32;
}

struct Rectangle { pub width: f32, pub height: f32 }
struct Triangle { pub side: f32 }
struct Circle { pub radius: f32 }

impl Shape for Rectangle {
    fn perimeter(&self) -> f32 {
        self.width * 2.0 + self.height * 2.0
    }
    fn area(&self) -> f32 {
        self.width * self.height
    }
}

impl Shape for Triangle {
    fn perimeter(&self) -> f32 {
        self.side * 3.0
    }
    fn area(&self) -> f32 {
        self.side * 0.5 * 3.0_f32.sqrt() / 2.0 * self.side
    }
}

impl Shape for Circle {
    fn perimeter(&self) -> f32 {
        self.radius * 2.0 * std::f32::consts::PI
    }
    fn area(&self) -> f32 {
        self.radius * self.radius * std::f32::consts::PI
    }
}

Trait可以翻譯為“特性”,“特征”,類似于其它GC語言中的接口或者協(xié)議,在Rust中也是一個多態(tài)的概念。Trait指定結(jié)構(gòu)體(Strut)必須實現(xiàn)的一組方法,然后它們可以為任意結(jié)構(gòu)體實現(xiàn),并且這些結(jié)構(gòu)可以在預期的特征中使用。

優(yōu)點

與枚舉相比,它們具有的一個主要優(yōu)點是,Trait可以被任意結(jié)構(gòu)體實現(xiàn),即使是不同的crate。當然,你也可以從其它crate中導入一個Trait,為你自己的結(jié)構(gòu)體去實現(xiàn)它,然后將該結(jié)構(gòu)體傳遞到需要該Traitcrate代碼中。另外,也可以選擇編寫接受某個Trait的代碼,這是使用枚舉所不能達到的效果。

缺點

不能快速通過Trait找到正在使用的變量并獲取它的其他屬性。

0x04 小結(jié)

本篇文章著重介紹了在Rust中兩種實現(xiàn)多態(tài)的方法——EnumTrait。就上面兩種方法而言,我個人還是比較推薦使用Trait的。使用Trait的靈活性非常強大。其實說起Trait,Rust為我們提供了兩種選擇,這個就留在下一篇文章講了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容