Rust特征

发布于:2024-05-05 ⋅ 阅读:(36) ⋅ 点赞:(0)

一、Rust特征是什么、怎么用

1、Rust特征是什么

我认为Rust特征和Java中的接口类似,但是扩展了语义

特征定义了一组可以被共享的行为,只要实现了特征,你就能使用这组行为

2、Rust特征怎么使用

(1)特征定义

pub trait Summary {//Summary 是特征名称
    fn summarize(&self) -> String;//和接口类似,不需要具体实现方法体
}

带有默认实现的特征定义

pub trait Summary {
    fn summarize(&self) -> String {
        String::from("(Read more...)")
    }
}

这样使得实现了该特征的类型,自动具备了默认的特征行为,当然也可以覆盖重写优先调用覆盖重写的特征行为

(2)为类型实现特征

pub trait Summary {
    fn summarize(&self) -> String;
}
pub struct Post {//类型可以不仅仅是结构体,是 类型 就可以定义特征
    pub title: String, // 标题
    pub author: String, // 作者
    pub content: String, // 内容
}

impl Summary for Post {
    fn summarize(&self) -> String {
        format!("文章{}, 作者是{}", self.title, self.author)
    }
}

pub struct Weibo {
    pub username: String,
    pub content: String
}

impl Summary for Weibo {//为Weibo 实现特征Summary,则Weibo具有Summary的特征和对应特征的行为
    fn summarize(&self) -> String {
        format!("{}发表了微博{}", self.username, self.content)
    }
}
fn main() {
  let post = Post{title: "Rust语言简介".to_string(),author: "Sunface".to_string(), content: "Rust棒极了!".to_string()};
  let weibo = Weibo{username: "sunface".to_string(),content: "好像微博没Tweet好用".to_string()};

  println!("{}",post.summarize());
  println!("{}",weibo.summarize());
}

为类型实现特征之后,我们来调用对应特征的行为来看看

文章Rust语言简介, 作者是Sunface
sunface发表了微博好像微博没Tweet好用

这样我们就完成了特征的基本定义和使用

(3)使用特征作为函数参数

这时候感觉特征有点多态的味道了,但不是Java继承的那种多态,像是Golang的那种组合多态。

可以使用任何实现了 Summary 特征的类型作为该函数的参数

pub trait Summary {
  fn summarize(&self) -> String;
}
pub struct Post {
  pub title: String, // 标题
  pub author: String, // 作者
  pub content: String, // 内容
}

impl Summary for Post {
  fn summarize(&self) -> String {
      format!("文章{}, 作者是{}", self.title, self.author)
  }
}

pub struct Weibo {
  pub username: String,
  pub content: String
}

impl Summary for Weibo {
  fn summarize(&self) -> String {
      format!("{}发表了微博{}", self.username, self.content)
  }
}
fn main() {
  let post = Post{title: "Rust语言简介".to_string(),author: "Sunface".to_string(), content: "Rust棒极了!".to_string()};
  notify(&post);
}

pub fn notify(item: &impl Summary) {
  println!("Breaking news! {}", item.summarize());
}

如果我们用特征作为返回值会怎么样呢,后面我们在讨论这件事情

(4)特征约束

其实impl Summary就是特质约束的一种语法糖写法,约束了你传递的参数需要实现Summary特征。
当然还可以是别的写法,形如 T: Summary 被称为特征约束,例如:

pub fn notify<T: Summary>(item: &T) {
    println!("Breaking news! {}", item.summarize());
}

如果涉及多个约束条件,那么我们可以这么写

pub fn notify(item: &(impl Summary + Display)) {}//Summary和Display两个约束
pub fn notify<T: Summary + Display>(item: &T) {}
fn some_function<T, U>(t: &T, u: &U) -> i32
    where T: Display + Clone,
          U: Clone + Debug
{}

前面的写法意义上是一样的,只是语法不同。

(5)使用特征约束有条件地实现方法或特征

特征约束,可以让我们在指定类型 + 指定特征的条件下去实现方法,例如:

use std::fmt::Display;

struct Pair<T> {
    x: T,
    y: T,
}

impl<T> Pair<T> {
    fn new(x: T, y: T) -> Self {
        Self {
            x,
            y,
        }
    }
}

impl<T: Display + PartialOrd> Pair<T> {
    fn cmp_display(&self) {
        if self.x >= self.y {
            println!("The largest member is x = {}", self.x);
        } else {
            println!("The largest member is y = {}", self.y);
        }
    }
}

上面代码的意思是

cmp_display 方法,并不是所有的 Pair 结构体对象都可以拥有,只有 T 同时实现了 Display +
PartialOrd 的 Pair 才可以拥有此方法

(5)派生特征(引入特征)

形如 #[derive(Debug)] ,这种是一种特征派生语法,被 derive 标记的对象会自动实现对应的默认特征代码继承相应的功能

// 为Centimeters引入结构体比较的特征,使得Centimeters可以进行比较
#[derive(PartialEq, PartialOrd)]
struct Centimeters(f64);

// 为Inches引入结构体打印的特征
#[derive(Debug)]
struct Inches(i32);

impl Inches {
    fn to_centimeters(&self) -> Centimeters {
        let &Inches(inches) = self;

        Centimeters(inches as f64 * 2.54)
    }
}

// 引入结构体打印、结构体比较的特征功能

#[derive(Debug,PartialEq,PartialOrd)]
struct Seconds(i32);

fn main() {
    let _one_second = Seconds(1);

    println!("One second looks like: {:?}", _one_second);
    let _this_is_true = (_one_second == _one_second);
    let _this_is_false = (_one_second > _one_second);

    let foot = Inches(12);

    println!("One foot equals {:?}", foot);

    let meter = Centimeters(100.0);

    let cmp =
        if foot.to_centimeters() < meter {
            "smaller"
        } else {
            "bigger"
        };

    println!("One foot is {} than one meter.", cmp);
}

(6)使用特征作为返回参数?

前面我们提到了特征可以参数函数参数进行传递,实现了类似Golang组合继承的效果,如果我们采用特征作为返回参数可以吗?
来看一段代码

pub trait Summary {
  fn summarize(&self) -> String;
}
pub struct Post {
  pub title: String, // 标题
  pub author: String, // 作者
  pub content: String, // 内容
}

impl Summary for Post {
  fn summarize(&self) -> String {
      format!("文章{}, 作者是{}", self.title, self.author)
  }
}

pub struct Weibo {
  pub username: String,
  pub content: String
}

impl Summary for Weibo {
  fn summarize(&self) -> String {
      format!("{}发表了微博{}", self.username, self.content)
  }
}
fn returns_summarizable(switch: bool) -> impl Summary {
  if switch {
      Post {
          title: String::from(
              "Penguins win the Stanley Cup Championship!",
          ),
          author: String::from("Iceburgh"),
          content: String::from(
              "The Pittsburgh Penguins once again are the best \
               hockey team in the NHL.",
          ),
      }
  } else {
      Weibo {
          username: String::from("horse_ebooks"),
          content: String::from(
              "of course, as you probably already know, people",
          ),
      }
  }
}
fn main() {
 
  _a =returns_summarizable(true);
}




上面代码中,Weibo和Post都已经实现了Summary特征,运行一下

`if` and `else` have incompatible types
expected struct `Post`, found struct `Weibo`

报错提示我们 if 和 else 返回了不同的类型。只有相同类型,才可以使用imply trait的方式。如果想要实现返回 “实现相同特征”的 “不同的类型”,需要使用特征对象,下一篇我们再讲特征对象


网站公告

今日签到

点亮在社区的每一天
去签到