iT邦幫忙

2022 iThome 鐵人賽

DAY 16
0

環境

OS: Windows 10
Editor: Visual Studio Code
Rust version: 1.63.0

特徵

以下來自教學文件的一句話:

注意:特徵類似於其他語言常稱作介面(interfaces)的功能,但還是有些差異。

確實,先前我是把特徵類比成其他語言中的interface,今天來理解Rust中的ㄊ中的特徵(trait)是什麼樣的功能。

定義特徵

以下是一個範例,我們定義幾個多邊形,然後一個叫做Drawable的特徵:

struct Vertex {
    x: f32,
    y: f32,
}

struct Rectangle {
    origin: Vertex,
    width: f32,
    height: f32,
}

struct Circle {
    origin: Vertex,
    radius: f32,
}

trait Drawable {
    fn draw(&self);
}

以下是矩形跟圓形對Drawable的實作:

impl Drawable for Rectangle {
    fn draw(&self) {
        println!(
            "I'm rectangle. My origin is ({}, {}). My width is {} and height is {}.",
            self.origin.x, self.origin.y, self.width, self.height
        );
    }
}

impl Drawable for Circle {
    fn draw(&self) {
        println!(
            "I'm circle. My origin is ({}, {}). My radius is {}.",
            self.origin.x, self.origin.y, self.radius
        );
    }
}

// ...

fn main() {
    let rect = Rectangle {
        origin: Vertex { x: 0.0, y: 0.0 },
        width: 4.0,
        height: 3.0,
    };

    let circle = Circle {
        origin: Vertex { x: 5.0, y: 5.0 },
        radius: 3.0,
    };

    rect.draw();
    circle.draw();
}

也可以預設特徵的實作:

trait Drawable {
    fn draw(&self) {
        println!("Nothing!");
    }
}

下面定義一個空的結構,空的結構去實作預設特徵的話,是可以直接使用的:

struct Nothing;

trait Drawable {
    fn draw(&self) {
        println!("Nothing!");
    }
}

impl Drawable for Nothing {}

// ... 

// in main function
let nothing = Nothing;
nothing.draw();

// output:
//    Nothing!

以特徵做為參數

同其他語言,例如C#,我們可以把特徵當作參數,傳進函式內,表示有實作這個特徵的型別才可以使用:

fn draw_shape(shape: &impl Drawable) {
    shape.draw();
}

// ...

draw_shape(&rect);

特徵界線(Trait bound)

昨天介紹泛型的時候,對於max的實作,我們可以加上限制,也就是對這個型別是否有實作特徵

// `+`表示多個限制
fn max<T: PartialOrd + Copy>(list: &[T]) -> T {
    let mut max = list[0];
    for &item in list {
        if item > max {
            max = item;
        }
    }

    max
}

當然,可以把特徵作為參數,也等同於做了限制:

// 回傳的值,也是標示有實作這兩個特徵的型別
fn max(list: &[(impl PartialOrd + Copy)]) -> (impl PartialOrd + Copy) {
    let mut max = list[0];
    for &item in list {
        if item > max {
            max = item;
        }
    }

    max
}

也可以使用where,可以更明瞭的知道限制了那些特徵:

fn max<T>(list: &[T]) -> T
where T: 
    Copy + PartialOrd
{
    let mut max = list[0];
    for &item in list {
        if item > max {
            max = item;
        }
    }

    max
}

實作std::fmt::Display

關於上面RectangleCircleDrawable的實作,需要把origin拆開來,是因為我們並不能直接輸出Vertex型別,因為沒有對它做std::fmt::Display的實作,於是把它加上實作:

use std::fmt;

impl fmt::Display for Vertex {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> fmt::Result {
        write!(f, "({}, {})", self.x, self.y)
    }
}

這樣就可以把Rectangle的實作改成這樣,Circle同理:

impl Drawable for Rectangle {
    fn draw(&self) {
        println!(
            "I'm rectangle. My origin is {}. My width is {} and height is {}.",
            self.origin, self.width, self.height
        );
    }
}

Reference


上一篇
[Rust] 泛型 (Generics)
下一篇
[Rust] 生命週期(Lifetime)
系列文
嘗試30天學「不」會Rust18
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言