rust重载运算符

1. 运算符重载

在上一篇文章《rust泛型编程范式》中我们讲了关联类型,例子如下

pub trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
}

对于运算符重载,有些运算符trait定义了关联类型,有些没有定义了,什么时候用关联类型什么时候不用关联类型,我们分别看看,最后得出结论。

2. 带有关联类型的重载

2.1 一元运算符

  • std::ops::Neg
  • std::ops::Not

其定义如下:

trait Neg {
    type Output;
    fn neg(self) -> Self::Output;
}

trait Not {
    type Output;
    fn not(self) -> Self::Output;
}

一个例子如下:

#[derive(Clone, Copy, Debug)]
struct Complex<T> {
    /// 复数的实部
    re: T,

    /// 复数的虚部
    im: T,
}

use std::ops::Neg;

impl<T> Neg for Complex<T>
where
    T: Neg<Output = T>,
{
    type Output = Complex<T>;
    fn neg(self) -> Complex<T> {
        Complex {
            re: -self.re,
            im: -self.im,
        }
    }
}

2.1 二元运算符

  • std::ops::Add
  • std::ops::Sub
  • std::ops::Mul
  • std::ops::Div
  • std::ops::Rem
  • std::ops::BitAnd
  • std::ops::BitOr
  • std::ops::BitXor
  • std::ops::Shl
  • std::ops::Shr

其trait定义如下:

trait Add<Rhs = Self> {
    type Output;
    fn add(self, rhs: Rhs) -> Self::Output;
}

trait BitXor<Rhs = Self> {
    type Output;
    fn bitxor(self, rhs: Rhs) -> Self::Output;
}

一个例子如下:

use std::ops::Add;

impl Add for Complex<i32> {
    type Output = Complex<i32>;
    fn add(self, rhs: Self) -> Self {
        Complex {
            re: self.re + rhs.re,
            im: self.im + rhs.im,
        }
    }
}

2.3 索引(非mut)

  • std::ops::Index

其trait定义如下

trait Index<Idx> {
    type Output: ?Sized;
    fn index(&self, index: Idx) -> &Self::Output;
}

3. 不带关联类型的重载

3.1 复合赋值运算符

  • std::ops::AddAssign
  • std::ops::SubAssign
  • std::ops::MulAssign
  • std::ops::DivAssign
  • std::ops::RemAssign
  • std::ops::BitAndAssign
  • std::ops::BitOrAssign
  • std::ops::BitXorAssign
  • std::ops::ShlAssign
  • std::ops::ShrAssign

其trait定义如下

trait AddAssign<Rhs = Self> {
    fn add_assign(&mut self, rhs: Rhs);
}

一个例子如下:

use std::ops::AddAssign;

impl<T> AddAssign for Complex<T>
where
    T: AddAssign<T>,
{
    fn add_assign(&mut self, rhs: Complex<T>) {
        self.re += rhs.re;
        self.im += rhs.im;
    }
}

3.2 比较运算符

  • std::cmp::PartialEq
  • std::cmp::PartialOrd

其trait定义如下:

trait PartialEq<Rhs = Self>
where
    Rhs: ?Sized,
{
    fn eq(&self, other: &Rhs) -> bool;
    fn ne(&self, other: &Rhs) -> bool {
        !self.eq(other)
    }
}

trait PartialOrd<Rhs = Self>: PartialEq<Rhs>
where
    Rhs: ?Sized,
{
    fn partial_cmp(&self, other: &Rhs) -> Option<Ordering>;
    fn lt(&self, other: &Rhs) -> bool { ... }
    fn le(&self, other: &Rhs) -> bool { ... }
    fn gt(&self, other: &Rhs) -> bool { ... }
    fn ge(&self, other: &Rhs) -> bool { ... }
}

一个例子如下:

impl<T: PartialEq> PartialEq for Complex<T> {
    fn eq(&self, other: &Complex<T>) -> bool {
        self.re == other.re && self.im == other.im
    }
}

3.3 索引(mut)

  • std::ops::IndexMut

其trait定义如下

trait IndexMut<Idx>: Index<Idx> {
    fn index_mut(&mut self, index: Idx) -> &mut Self::Output;
}

4 小结

什么时候用关联类型,观察上述trait定义,有两个重要发现:

  • 当存在返回值且返回值为泛型定义是,定义了关联类型。
  • 当不需要泛型返回值或者无返回值,就没有关联类型,无返回值一般是修改自身,因此参数是&mut self。

按照这两个发现,我们可以规范一下自己何时用trait的关联类型。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容