Trait的基本用法

Trait最基本的作用是從多種類型中抽取出共性的屬性或方法,並定義這些方法的規範(即方法簽名)。

例如,對於Audio類型和Video類型,它們有幾個具有共性的方法:

  • play方法用於播放
  • pause方法用於暫停
  • get_duration方法用於顯示媒體的總時長

為了抽取這些共性方法,可定義一個名為Playable的Trait,並在其中規範好這些方法的簽名。

自定義Trait類型時,使用trait關鍵字。如:


#![allow(unused)]
fn main() {
trait Playable {
  fn play(&self);
  fn pause(&self) {
    println!("pause");
  }
  fn get_duration(&self) -> f32;
}
}

注意上面的play方法和get_duration方法都僅僅只規範了它們的方法簽名,並沒有為它們定義方法體,而pause方法則指定了函數簽名且定義了方法體,這個方法體是pause方法的默認方法體。

定義好Playable Trait後,先讓Audio類型去實現Playable:


#![allow(unused)]
fn main() {
struct Audio {
  name: String,
  duration: f32,
}

impl Playable for Audio {
  fn play(&self) {
    println!("listening audio: {}", self.name);
  }
  fn get_duration(&self) -> f32 {
    self.duration
  }
}
}

注意,上面impl Playable for Audio表示為Audio類型實現Playable Trait。Audio實現Playable Trait時,Trait中的所有沒有提供默認方法體的方法(即play方法和get_duration方法)都需要實現。對於提供了默認方法體的方法,可實現可不實現,如果實現了則覆蓋默認方法體,如果沒有實現,則使用默認方法體。

下面再為Video類型實現Playable Trait,這裡也實現了有默認方法體的pause方法:


#![allow(unused)]
fn main() {
struct Video {
  name: String,
  duration: f32,
}

impl Playable for Video {
  fn play(&self) {
    println!("watching video: {}", self.name);
  }
  fn pause(&self) {
    println!("video paused");
  }
  fn get_duration(&self) -> f32 {
    self.duration
  }
}
}

當Audio類型和Video類型實現了Playable Trait後,這兩個類型的實例對象自然可以去調用它們各自定義的方法。而對於Audio沒有定義的pause方法,則會從其所實現的Trait中尋找。

fn main() {
  let audio = Audio{
    name: "telephone.mp3".to_string(),
    duration: 4.32,
  };
  audio.play();
  audio.pause();
  println!("{}", audio.get_duration());

  let video = Video {
    name: "Yui Hatano.mp4".to_string(),
    duration: 59.59,
  };
  video.play();
  video.pause();
  println!("{}", video.get_duration());
}