Rust 常用 trait 实现 (上)

Eq & PartialEq

  • 符号:==!=

  • 区别:Eq 相比于 PartialEq 还需额外满足反身性,即 a == a。对于浮点类型,Rust 只实现了 PartialEq 而不是
    Eq,原因就是 NaN != NaN

PartialEq

可以直接使用 #[derive(PartialEq)] 派生宏交由编译器实现,对于 struct,Rust 会逐字段比较,对于 enum,会对 enum 中的数据进行比较。我们也可以自己实现该 trait。

struct Book {
  name: String,
  price: usize,
}

impl PartialEq for Book {
  fn eq(&self, other: &Self) -> bool {
    self.name == other.name
  }
}

Eq

实现 Eq 的前提是已经实现了 PartialEq,如果已经实现了 PartialEq,可以直接使用 #[derive(Eq)],但是当 struct 或者 enum 中存在浮点数时,我们需要手动实现该 trait。

#[derive(PartialEq)]
struct Book {
  name: String,
  price: f32
}

impl Eq for Book {} // 不用怀疑,就是这么简单~

Ord & PartialOrd

  • 符号:<>>=

  • 区别:PartialOrd 不许满足连通性(a⇐b或a>=b),只需满足反对称性(a ⇐ b 且 a >= b 可推出 a == b)和传递性(a ⇐ b 且 b ⇐ c 可推出 a ⇐ c)。

PartialOrd

Ord & PartialOrd 均可通过 #[derive] 派生宏交由编译器自动实现,当使用派生宏实现后,将会基于 struct 的字段声明以字典序进行比较,遇到枚举中的数据也会以此类推。注意,实现 PartialOrd 要求该类型实现 PartialEq

struct Person {
  name: String,
  age: usize,
}

impl PartialEq for Person {
  fn eq(&self, other: &Self) -> bool {
    self.age == other.age
  }
}

impl PartialOrd for Person {
  fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
    self.age.partial_cmp(&other.age)
  }
}

你可能注意到了 partial_cmp 函数的返回值是一个Option,之所以这么做是因为有时可能会与 NaN 作比较。

Ord

Ord 要求你的类型实现 PartialOrdEq(因此 PartialEq也需要被实现),实现 PartialEqPartialOrd 以及 Ord 时要特别注意彼此之间不能有冲突(即比较的内容要一致)。浮点型实现了PartialOrd但是没有实现Ord,因为NaN < 0 == falseNaN >= 0 == false都为真。

/* ... */
impl Eq for Person {}

impl Ord for Person {
  fn cmp(&self, other: &Self) -> std::cmp::Ordering {
    self.age.cmp(&other.age)
  }
}

完成 PartialOrd 后会为你的类型提供lt()le()gt()ge()的比较操作。

完成 Ord 后会为你的类型提供 max()min()clamp()(比较该值是否在某个区间内)。

From & Into

我们都知道 Rust 中的 as可以用于一些基本类型的简单转换,如果涉及到自定义类型的转换,我们可以为它们实现FromInto trait 以更方便地转换类型。

我们先定义如下两个结构体。

struct Dog {
  weight: usize,
  height: usize,
}

struct People {
  iq: isize,
  eq: isize,
}

From

实现了 From<Dog> trait 后会为你的类型提供 from()into()try_from()try_into() 方法,使用 try_from()try_into() 方法始终是正确的,使用 into()try_into()方法时要写上类型注解,否则 Rust 不知道你想转换成什么类型。

impl From<Dog> for People {
  fn from(d: Dog) -> Self {
    People {
      iq: d.weight as isize + 10,
      eq: -1 * d.height as isize,
    }
  }
}

fn main() {
  let d1 = Dog {weight: 20, height: 20};
  let d2 = Dog {weight: 30, height: 30};
  let p1 = People::from(d);
  let p2: People = d2.into();
}

Into

如果你想将 Dog 转为 People 类型,只实现 From 或者 Into trait 即可,否则会造成矛盾。

impl Into<People> for Dog {
  fn into(self) -> People {
    People {
      iq: self.weight as isize + 10,
      eq: -1 * self.height as isize,
    }
  }
}

fn main() {
  // ... 和from一样的用法
}

TryFrom & TryInto

这一组 trait 作用和上一组相同,区别是这一组通常用于转换容易出错的类型。

TryFrom

实现了 TryFrom trait 后会自动为你的类型实现 try_from()try_into()

impl TryFrom<Dog> for People {
  type Error = String;

  fn try_from(value: Dog) -> Result<Self, Self::Error> {
    if value.height < 0 as usize || value.weight < 0 as usize {
      Err(String::from("转换出错 => Dog to People"))
    } else {
      Ok(People {
        iq: value.weight as isize,
        eq: value.height as isize,
      })
    }
  }
}

fn main() {
  let d1 = Dog{weight: 20, height: 20};
  let d2 = Dog{weight: 20, height: 20};
  let p1 = People::try_from(d).unwrap();
  let p2 = d2.try_into().unwrap();
}

TryInto

实现了 TryInto trait 后会自动为你的类型提供 try_into() 方法。

impl TryInto<People> for Dog {
  type Error = ();

  fn try_into(self) -> Result<People, Self::Error> {
    Err(())
  }
}

fn main() {
  let d = Dog {
    weight: 20,
    height: 20,
  };
  let p: People = d.try_into().unwrap();
}

ToString & FromStr

ToString

要把任何类型转换成 String,只需要实现那个类型的 ToString trait。然而不要直接这么做,您应该实现 fmt::Display trait,它会自动提供 ToString,并且还可以用来打印类型。

use std::fmt;

struct Circle {
    radius: i32
}

impl fmt::Display for Circle {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Circle of radius {}", self.radius)
    }
}

// ToString版本实现
impl ToString for Circle {
    fn to_string(&self) -> String {
        format!("Circle of radius {:?}", self.radius)
    }
}

fn main() {
    let circle = Circle { radius: 6 };
    println!("{}", circle.to_string());
}

FromStr

#[derive(Debug)]
struct People {
  name: String,
  age: usize,
}

impl std::str::FromStr for People {
  type Err = ();

  fn from_str(s: &str) -> Result<Self, Self::Err> {
    let ss: Vec<&str> = s.split(";").collect();
    if ss.len() == 2 {
      match ss[1].parse::<usize>() {
        | Ok(age) => Ok(People {
          name: ss[0].to_string(),
          age: age,
        }),
        | Err(e) => Err(()),
      }
    } else {
      Err(())
    }
  }
}

fn main() {
  let s = "Mr.Li;20";
  println!("{:?}", s.parse::<People>()); // Ok(People { name: "Mr.Li", age: 20 })
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值