Rust 常用 trait 实现

1. 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 {} // 不用怀疑,就是这么简单~

2.Ord & PartialOrd

  • 符号:<、⇐、>、>=
  • 区别:PartialOrd 不许满足连通性(a⇐b或a>=b),只需满足反对称性(a ⇐ b 且 a >= b 可推出 a == b)和传递性(a ⇐ b 且 b ⇐ c 可推出 a ⇐ c)。
  • 完成 PartialOrd 后会为你的类型提供lt()、le()、gt()、ge()的比较操作。
  • 完成 Ord 后会为你的类型提供 max()、min() 和 clamp()(比较该值是否在某个区间内)。

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 {
    self.age.partial_cmp(&other.age)
  }
}

Ord

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

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

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

3.From & Into

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

定义如下两个结构体:

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

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

From

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

impl From 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 for Dog {
  fn into(self) -> People {
    People {
      iq: self.weight as isize + 10,
      eq: -1 * self.height as isize,
    }
  }
}

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

4.TryFrom & TryInto

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

TryFrom

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

impl TryFrom for People {
  type Error = String;

  fn try_from(value: Dog) -> Result {
    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 for Dog {
  type Error = ();

  fn try_into(self) -> Result {
    Err(())
  }
}

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

5.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 {
    let ss: Vec<&str> = s.split(";").collect();
    if ss.len() == 2 {
      match ss[1].parse::() {
        | 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::()); // Ok(People { name: "Mr.Li", age: 20 })
}

你可能感兴趣的:(Rust,rust,开发语言,后端)