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());
}